/**********************************************************************
 * File:        imgs.c  (Formerly images.c)
 * Description: Main image manipulation functions.
 * Author:      Ray Smith
 * Created:     Thu Jun 07 16:25:02 BST 1990
 *
 * (C) Copyright 1990, Hewlett-Packard Ltd.
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 ** http://www.apache.org/licenses/LICENSE-2.0
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 *
 **********************************************************************/

#include          "mfcpch.h"     //precompiled headers
#ifdef __MSW32__
#include          <io.h>
#else
#include          <unistd.h>
#endif
#include          <string.h>
#ifdef __UNIX__
#include          <assert.h>
#endif

// Include automatically generated configuration file if running autoconf.
#ifdef HAVE_CONFIG_H
#include "config_auto.h"
#endif

#ifdef HAVE_LIBLEPT
// Include leptonica library only if autoconf (or makefile etc) tell us to.
#include "allheaders.h"
#endif

#include          "stderr.h"
#include          "tprintf.h"
#include          "imgerrs.h"
#include          "memry.h"
#include          "imgs.h"
#include          "imgio.h"
#include          "imgunpk.h"

#include <fstream>				//Fuad Sep 16, 2008
#include <map>
#include <list>
#include "DisjointSets_FJ.h"



#define FIXED_COLOURS   32       /*number of fixed colours */
#define MIN_4BIT      48         /*4bpp range */
#define MAX_4BIT      64
#define MIN_6BIT      64         /*6bpp range */
#define MAX_6BIT      128
#define BLACK_PIX     0

static uinT8 grey_scales[FIXED_COLOURS] = {
  0, 255, 76, 227, 151, 179, 28, 104,
  149, 72, 215, 67, 53, 44, 156, 137,
  110, 153, 79, 181, 166, 218, 55, 81,
  129, 105, 179, 149, 168, 69, 84, 126
};

#undef EXTERN
#define EXTERN

EXTERN INT_VAR (image_default_resolution, 300, "Image resolution dpi");

/**********************************************************************
 * IMAGE
 *
 * Contructor for an IMAGE class. Makes the image definitely illegal.
 **********************************************************************/

IMAGE::IMAGE() {  //construct an image
  bpp = 0;                       //all illegal
  fd = -1;
  image = NULL;
  photo_interp = 1;
  res = image_default_resolution;
}


/**********************************************************************
 * IMAGE::operator=
 *
 * Assign an IMAGE to another. The dest becomes the owner of the memory.
 **********************************************************************/

IMAGE & IMAGE::operator= (       //assignment
IMAGE & source                   //source image
) {
  destroy();
  bpp = source.bpp;
  photo_interp = source.photo_interp;
  bps = source.bps;
  bytespp = (bpp + 7) / 8;
  lineskip = source.lineskip;    //copy everything
  captured = source.captured;
  xsize = source.xsize;
  ysize = source.ysize;
  res = source.res;
  image = source.image;
  xdim = source.xdim;
  bufheight = source.bufheight;
  fd = source.fd;
  reader = source.reader;
  ymin = source.ymin;
  ymax = source.ymax;

  source.captured = TRUE;        //source now captured
  source.fd = -1;

  return *this;
}


/**********************************************************************
 * create
 *
 * Create an image (allocate memory) of a specific size and bpp.
 **********************************************************************/

inT8 IMAGE::create(                     //get rest of image
                   inT32 x,             //x size required
                   inT32 y,             //ysize required
                   inT8 bits_per_pixel  //bpp required
                  ) {
  uinT8 *pixels;                 //memory for image

  xdim = check_legal_image_size (x, y, bits_per_pixel);
  if (xdim < 0)
    return -1;
  pixels = (uinT8 *) alloc_big_zeros ((size_t) (xdim * y * sizeof (uinT8)));
  if (pixels == NULL) {
    MEMORY_OUT.error ("IMAGE::create", ABORT, "Size=(%d,%d)", xdim, y);
    return -1;
  }
                                 //allocate to image
  this->capture (pixels, x, y, bits_per_pixel);
  captured = FALSE;
  res = image_default_resolution;
  return 0;                      //success
}


/**********************************************************************
 * destroy
 *
 * Destroy an image, freeing memory and closing any open file.
 **********************************************************************/

void IMAGE::destroy() {  //get rid of image
  if (image != NULL && !captured) {
    free_big_mem(image);
  }
  image = NULL;
  if (fd >= 0) {
    close(fd);
    fd = -1;
  }
  bpp = 0;
}


/**********************************************************************
 * capture
 *
 * Assign a given memory area to an image to use as an image of
 * given size and bpp.
 **********************************************************************/

inT8 IMAGE::capture(                     //get rest of image
                    uinT8 *pixels,       //image memory
                    inT32 x,             //x size required
                    inT32 y,             //ysize required
                    inT8 bits_per_pixel  //bpp required
                   ) {
  destroy();
  xdim = check_legal_image_size (x, y, bits_per_pixel);
  if (xdim < 0)
    return -1;
  xsize = x;
  ysize = y;
  bufheight = y;
  bpp = bits_per_pixel;
  bps = bpp == 24 ? 8 : bpp;
  photo_interp = 1;
  bytespp = (bpp + 7) / 8;
  image = pixels;                //assign image area
  ymin = 0;
  ymax = bufheight;              //read it all
  captured = TRUE;
  res = image_default_resolution;
  return 0;                      //success
}


/**********************************************************************
 * pixel
 *
 * Get a single pixel out of the image.
 **********************************************************************/

uinT8 IMAGE::pixel(          //get rest of image
                   inT32 x,  //x coord
                   inT32 y   //y coord
                  ) {
  if (x < 0)
    x = 0;                       //silently clip
  else if (x >= xsize)
    x = xsize - 1;
  if (y < 0)
    y = 0;
  else if (y >= ysize)
    y = ysize - 1;
  check_legal_access (x, y, 1);
  switch (bpp) {
    case 5:
    case 6:
    case 8:
      return image[(ymax - 1 - y) * xdim + x];
    case 4:
      return bpp4table[image[(ymax - 1 - y) * xdim + x / 2]][x & 1];
    case 2:
      return bpp2table[image[(ymax - 1 - y) * xdim + x / 4]][x & 3];
    case 1:
      return bpp1table[image[(ymax - 1 - y) * xdim + x / 8]][x & 7];
    default:
      tprintf ("Unexpected bits per pixel %d\n", bpp);
      return 0;
  }
}


/**********************************************************************
 * check_legal_image_size
 *
 * Check that the supplied image sizes are legal.  If they are,
 * the xdim is returned, else -1.
 **********************************************************************/

inT32 check_legal_image_size(                     //get rest of image
                             inT32 x,             //x size required
                             inT32 y,             //ysize required
                             inT8 bits_per_pixel  //bpp required
                            ) {
  if (x <= 0 || y <= 0) {
    BADIMAGESIZE.error ("check_legal_image_size", TESSLOG, "(%d,%d)", x, y);
    return -1;                   //failed
  }
  if (bits_per_pixel != 1 && bits_per_pixel != 2
    && bits_per_pixel != 4 && bits_per_pixel != 5
  && bits_per_pixel != 6 && bits_per_pixel != 8 && bits_per_pixel != 24) {
    BADBPP.error ("check_legal_image_size", TESSLOG, "%d", bits_per_pixel);
    return -1;
  }
                                 //bytes per line
  return COMPUTE_IMAGE_XDIM (x, bits_per_pixel);
}


/**********************************************************************
 * copy_sub_image
 *
 * Copy a portion of one image to a portion of another image.
 * If the bpps are different, the position of the most significant
 * bit is preserved.
 **********************************************************************/

DLLSYM void copy_sub_image(                   //copy rectangle
                           IMAGE *source,     //source image
                           inT32 xstart,      //start coords
                           inT32 ystart,
                           inT32 xext,        //extent to copy
                           inT32 yext,
                           IMAGE *dest,       //destination image
                           inT32 xdest,       //destination coords
                           inT32 ydest,
                           BOOL8 adjust_grey  //shift to new bpp
                          ) {
  IMAGELINE copyline;            //copy of line
  uinT8 *copy;                   //source pointer
  inT8 shift;                    //shift factor
  inT32 pixel;                   //pixel index
  inT32 y;                       //line index
  inT32 yoffset;                 //current adjusted offset
  inT32 bytesize;                //no of bytes to copy
  inT32 srcppb;                  //pixels per byte
  BOOL8 aligned;

  if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
    return;
  if (xext <= 0)
    xext = source->xsize;        //default to all
  if (xext > source->xsize - xstart)
                                 //clip to smallest
      xext = source->xsize - xstart;
  if (xext > dest->xsize - xdest)
    xext = dest->xsize - xdest;
  if (yext <= 0)
    yext = source->ysize;        //default to all
  if (yext > source->ysize - ystart)
                                 //clip to smallest
      yext = source->ysize - ystart;
  if (yext > dest->ysize - ydest)
    yext = dest->ysize - ydest;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

  srcppb = 8 / source->bpp;      //pixels per byte
  if (source->bpp == dest->bpp || !adjust_grey)
    shift = 0;                   //no adjustment
  else {
    shift = source->bps - dest->bps;
    if (shift < 0)
      shift = -shift;            //keep positive
  }
  aligned = source->bpp == dest->bpp;
  if (aligned && srcppb != 0) {
    aligned = xstart % srcppb == 0
      && xdest % srcppb == 0
      && (xext % srcppb == 0 || xdest + xext == dest->xsize);
  }
  for (y = 0; y < yext; y++) {
    if (ystart >= ydest)
      yoffset = y;               //top down
    else
      yoffset = yext - y - 1;    //bottom up
    source->check_legal_access (xstart, ystart + yoffset, xext);
    dest->check_legal_access (xdest, ydest + yoffset, xext);
    if (aligned) {
      bytesize = COMPUTE_IMAGE_XDIM (xext, source->bpp);
      //get bytes per line
      if (srcppb == 0)
                                 //do cheap move
        memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest * 3, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart * 3, (unsigned) bytesize);
      else
                                 //do cheap move
        memmove (dest->image + (dest->ymax - 1 - ydest - yoffset) * dest->xdim + xdest / srcppb, source->image + (source->ymax - 1 - ystart - yoffset) * source->xdim + xstart / srcppb, (unsigned) bytesize);
    }
    else {
      if (shift == 0) {
        source->fast_get_line (xstart, ystart + yoffset, xext,
          &copyline);
      }
      else if (source->bpp < dest->bpp) {
        source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
        if (source->bpp <= shift
        && (source->bpp == 1 || source->bpp == 4)) {
          if (source->bpp == 1) {
            for (pixel = 0, copy = copyline.pixels; pixel < xext;
              pixel++, copy++)
            if (*copy)
              *copy = 0xff;
          }
          else {
            for (pixel = 0, copy = copyline.pixels; pixel < xext;
              pixel++, copy++)
                                 //scale up
            *copy = (*copy << shift) | *copy;
          }
        }
        else {
          for (pixel = 0, copy = copyline.pixels; pixel < xext;
            pixel++)
          *copy++ <<= shift;     //scale up
        }
      }
      else {
        source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
        if (source->bpp == 24) {
          for (pixel = 0, copy = copyline.pixels + 1; pixel < xext;
          pixel++) {
            *copy >>= shift;
            copy += 3;
          }
        }
        else {
          for (pixel = 0, copy = copyline.pixels; pixel < xext;
            pixel++)
          *copy++ >>= shift;     //scale down
        }
      }
      dest->put_line (xdest, ydest + yoffset, xext, &copyline, 0);
    }
  }
}


/**********************************************************************
 * enlarge_sub_image
 *
 * Enlarge a portion of one image to a portion of another image.
 * If the bpps are different, the position of the most significant
 * bit is preserved.
 **********************************************************************/

DLLSYM void enlarge_sub_image(                   //enlarge rectangle
                              IMAGE *source,     //source image
                              inT32 xstart,      //scaled start coords
                              inT32 ystart,
                              IMAGE *dest,       //destination image
                              inT32 xdest,       //dest coords
                              inT32 ydest,
                              inT32 xext,        //destination extent
                              inT32 yext,
                              inT32 scale,       //scale factor
                              BOOL8 adjust_grey  //shift to new bpp
                             ) {
  inT8 shift;                    //shift factor
  uinT8 pixel;                   //current pixel
  inT32 srcext;                  //source extent
  inT32 xoffset;                 //column index
  inT32 yoffset;                 //line index
  inT32 xindex, yindex;          //index in super pixel
  inT32 startxindex;             //initial x index
  inT32 xscale;                  //x scale factor
  uinT8 *src;                    //source pixels
  uinT8 *destpix;                //dest pixels
  IMAGELINE copyline;            //copy of line
  IMAGELINE bigline;             //expanded line

  if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
    return;

  if (xext <= 0)
    xext = dest->xsize;          //default to all
  if (xext > source->xsize * scale - xstart)
                                 //clip to smallest
    xext = source->xsize * scale - xstart;
  if (xext > dest->xsize - xdest)
    xext = dest->xsize - xdest;
  if (yext <= 0)
    yext = dest->ysize;          //default to all
  if (yext > source->ysize * scale - ystart)
    yext = source->ysize * scale - ystart;
  if (yext > dest->ysize - ydest)
    yext = dest->ysize - ydest;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

  xindex = xstart % scale;       //offset in super pixel
  startxindex = xindex;
  yindex = ystart % scale;
                                 //no of source pixels
  srcext = (xext + xindex + scale - 1) / scale;
  xstart /= scale;               //actual start
  ystart /= scale;
  if (adjust_grey) {
    shift = dest->bps - source->bps;
  }
  else
    shift = 0;                   //no adjustment
  bigline.init (xext * 3);
  bigline.bpp = dest->bpp == 24 ? source->bpp : dest->bpp;

  for (yoffset = 0; yoffset < yext; ystart++) {
    source->check_legal_access (xstart, ystart, srcext);
    dest->check_legal_access (xdest, ydest + yoffset, xext);
    source->fast_get_line (xstart, ystart, srcext, &copyline);
    src = copyline.pixels;
    destpix = bigline.pixels;
    xscale = scale;              //enlargement factor
    if (source->bpp == 24 && dest->bpp == 24) {
      for (xoffset = 0, xindex = startxindex; xoffset < xext;
      src += source->bytespp) {
        xoffset += xscale - xindex;
        if (xoffset > xext)
          xscale -= xoffset - xext;
        for (; xindex < xscale; xindex++) {
          *destpix++ = *src;
          *destpix++ = *(src + 1);
          *destpix++ = *(src + 2);
        }
        xindex = 0;
      }
    }
    else {
      if (source->bpp == 24)
        src++;
      for (xoffset = 0, xindex = startxindex; xoffset < xext;
      src += source->bytespp) {
        xoffset += xscale - xindex;
        if (xoffset > xext)
                                 //clip to dest limit
            xscale -= xoffset - xext;
        if (shift == 0)
          pixel = *src;
        else if (shift > 0)
          pixel = *src << shift;
        else
          pixel = *src >> (-shift);
        for (; xindex < xscale; xindex++)
          *destpix++ = pixel;    //duplicate pixel
        xindex = 0;
      }
    }
    for (; yoffset < yext && yindex < scale; yindex++, yoffset++) {
      dest->put_line (xdest, ydest + yoffset, xext, &bigline, 0);
    }
    yindex = 0;
  }
}


/**********************************************************************
 * fast_reduce_sub_image
 *
 * Reduce a portion of one image to a portion of another image.
 * If the bpps are different, the position of the most significant
 * bit is preserved.
 * This is a fast but dirty version, which simply sub-samples.
 * It does not smooth as it reduces.
 **********************************************************************/

DLLSYM void fast_reduce_sub_image(                   //reduce rectangle
                                  IMAGE *source,     //source image
                                  inT32 xstart,      //start coords
                                  inT32 ystart,
                                  inT32 xext,        //extent to copy
                                  inT32 yext,
                                  IMAGE *dest,       //destination image
                                  inT32 xdest,       //destination coords
                                  inT32 ydest,
                                  inT32 scale,       //reduction factor
                                  BOOL8 adjust_grey  //shift to new bpp
                                 ) {
  inT8 shift;                    //shift factor
  inT32 xfactor;                 //run on x coord
  inT32 divisor;                 //total cell area
  inT32 xindex, yindex;          //into averaging square
  inT32 xcoord;                  //current x coord
  inT32 destext;                 //destination size
  inT32 yoffset;                 //current adjusted offset
  uinT8 *pixel;                  //ptr to source pixels
  inT32 *sums;                   //ptr to sums array
  IMAGELINE copyline;            //copy of line
  inT32 *linesums;               //averaging sums

  if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
    return;
  if (xext <= 0)
    xext = source->xsize;        //default to all
  if (xext > source->xsize - xstart)
                                 //clip to smallest
      xext = source->xsize - xstart;
  if (xext > (dest->xsize - xdest) * scale)
    xext = (dest->xsize - xdest) * scale;
  if (yext <= 0)
    yext = source->ysize;        //default to all
  if (yext > source->ysize - ystart)
                                 //clip to smallest
      yext = source->ysize - ystart;
  if (yext > (dest->ysize - ydest) * scale)
    yext = (dest->ysize - ydest) * scale;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

  xfactor = xext % scale;        //left overs
  if (xfactor == 0)
    xfactor = scale;
                                 //destination pixels
  destext = (xext + scale - 1) / scale;
  if (adjust_grey)
                                 //shift factor
    shift = dest->bps - source->bps;
  else
    shift = 0;                   //no adjustment
  linesums = new inT32[destext * source->bytespp];

  for (yoffset = 0; yoffset < yext; ydest++) {
    source->check_legal_access (xstart, ystart + yoffset, xext);
    dest->check_legal_access (xdest, ydest, destext);
    for (xindex = destext * source->bytespp - 1; xindex >= 0; xindex--)
      linesums[xindex] = 0;      //zero sums
    for (yindex = 0; yindex < scale
    && ystart + yoffset < source->ysize; yindex += 3) {
      source->fast_get_line (xstart, ystart + yoffset, xext, &copyline);
      pixel = copyline.pixels;   //start of line
      if (source->bpp == 24) {
        for (xcoord = 1, sums = linesums; xcoord < destext;
        xcoord++, sums += 3) {
          for (xindex = 0; xindex < scale; xindex += 2) {
            *sums += *pixel++;
            *(sums + 1) += *pixel++;
            *(sums + 2) += *pixel++;
            pixel += 3;
          }
          if (scale & 1)
            pixel -= 3;          //correct position
        }
        for (xindex = 0; xindex < xfactor; xindex += 2) {
          *sums += *pixel++;
          *(sums + 1) += *pixel++;
          *(sums + 2) += *pixel++;
          pixel += 3;
        }
      }
      else {
        for (xcoord = 1, sums = linesums; xcoord < destext;
        xcoord++, sums++) {
          for (xindex = 0; xindex < scale; xindex += 2) {
            *sums += *pixel;
            pixel += 2;
          }
          if (scale & 1)
            pixel--;             //correct position
        }
        for (xindex = 0; xindex < xfactor; xindex += 2) {
          *sums += *pixel;
          pixel += 2;
        }
      }
      yoffset += 3;              //every 3 lines
    }
    if (yindex > scale)
      yoffset -= yindex - scale; //back on right scale
    copyline.init ();            //set pixels back to array
    copyline.bpp = source->bpp;
    pixel = copyline.pixels;
                                 //pixels in block
    divisor = ((yindex + 2) / 3) * ((scale + 1) / 2);
    if (shift <= 0) {
      divisor <<= (-shift);      //do greyscale correction
      for (sums = linesums, xindex = (destext - 1) * source->bytespp;
        xindex > 0; xindex--)
                                 //turn to destination value
      *pixel++ = (uinT8) (*sums++ / divisor);
      for (xindex = source->bytespp; xindex > 0; xindex--)
        *pixel++ = *sums++
          / (((yindex + 2) / 3) * ((xfactor + 1) / 2) << (-shift));
      //lastone different
    }
    else {
      for (sums = linesums, xindex = (destext - 1) * source->bytespp;
        xindex > 0; xindex--)
      *pixel++ = (uinT8) ((*sums++ << shift) / divisor);
      //destination value
      for (xindex = source->bytespp; xindex > 0; xindex--)
                                 //last one different
        *pixel++ = (*(sums++) << shift) / (((yindex + 2) / 3) * ((xfactor + 1) / 2));
    }
                                 //put in destination
    dest->put_line (xdest, ydest, destext, &copyline, 0);
  }
  delete linesums;
}


/**********************************************************************
 * reduce_sub_image
 *
 * Reduce a portion of one image to a portion of another image.
 * If the bpps are different, the position of the most significant
 * bit is preserved.
 **********************************************************************/

DLLSYM void reduce_sub_image(                   //reduce rectangle
                             IMAGE *source,     //source image
                             inT32 xstart,      //start coords
                             inT32 ystart,
                             inT32 xext,        //extent to copy
                             inT32 yext,
                             IMAGE *dest,       //destination image
                             inT32 xdest,       //destination coords
                             inT32 ydest,
                             inT32 scale,       //reduction factor
                             BOOL8 adjust_grey  //shift to new bpp
                            ) {
  inT8 shift;                    //shift factor
  inT32 xfactor;                 //run on x coord
  inT32 divisor;                 //total cell area
  inT32 div2;                    //total cell area divided by 2
  inT32 xindex, yindex;          //into averaging square
  inT32 xcoord;                  //current x coord
  inT32 destext;                 //destination size
  inT32 yoffset;                 //current adjusted offset
  uinT8 *pixel;                  //ptr to source pixels
  inT32 *sums;                   //ptr to sums array
  IMAGELINE copyline;            //copy of line
  inT32 *linesums;               //averaging sums

  if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
    return;
  if (xext <= 0)
    xext = source->xsize;        //default to all
  if (xext > source->xsize - xstart)
                                 //clip to smallest
      xext = source->xsize - xstart;
  if (xext > (dest->xsize - xdest) * scale)
    xext = (dest->xsize - xdest) * scale;
  if (yext <= 0)
    yext = source->ysize;        //default to all
  if (yext > source->ysize - ystart)
                                 //clip to smallest
      yext = source->ysize - ystart;
  if (yext > (dest->ysize - ydest) * scale)
    yext = (dest->ysize - ydest) * scale;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

  xfactor = xext % scale;        //left overs
  if (xfactor == 0)
    xfactor = scale;
                                 //destination pixels
  destext = (xext + scale - 1) / scale;
  if (adjust_grey)
                                 //shift factor
    shift = dest->bps - source->bps;
  else
    shift = 0;                   //no adjustment
  linesums = new inT32[destext * source->bytespp];

  for (yoffset = 0; yoffset < yext; ydest++) {
    source->check_legal_access (xstart, ystart + yoffset, xext);
    dest->check_legal_access (xdest, ydest, destext);
    for (xindex = 0; xindex < (destext) * source->bytespp; xindex++)
      linesums[xindex] = 0;      //zero sums
    for (yindex = 0; yindex < scale && ystart + yoffset < source->ysize;
    yindex++) {
      source->fast_get_line (xstart, ystart + yoffset, xext, &copyline);
      pixel = copyline.pixels;   //start of line
      if (source->bpp == 24) {
        for (xcoord = 1, sums = linesums; xcoord < destext;
        xcoord++, sums += 3) {
          for (xindex = 0; xindex < scale; xindex++) {
            *sums += *pixel++;
            *(sums + 1) += *pixel++;
            *(sums + 2) += *pixel++;
          }
        }
        for (xindex = 0; xindex < xfactor; xindex++) {
          *sums += *pixel++;
          *(sums + 1) += *pixel++;
          *(sums + 2) += *pixel++;
        }
      }
      else {
        for (xcoord = 1, sums = linesums; xcoord < destext;
        xcoord++, sums++) {
          for (xindex = 0; xindex < scale; xindex++)
            *sums += *pixel++;
        }
        for (xindex = 0; xindex < xfactor; xindex++)
          *sums += *pixel++;
      }
      yoffset++;                 //next line
    }
    copyline.init ();            //set pixels back to array
    copyline.set_bpp (source->bpp);
    pixel = copyline.pixels;
    divisor = yindex * scale;
    if (divisor == 0) {
      tprintf
        ("Impossible:divisor=0!, yindex=%d, scale=%d, yoffset=%d,yext=%d\n",
        yindex, scale, yoffset, yext);
      break;
    }
    if (shift <= 0) {
      divisor <<= (-shift);      //do greyscale correction
      div2 = divisor / 2;
      for (sums = linesums, xindex = (destext - 1) * source->bytespp;
        xindex > 0; xindex--)
      *pixel++ = (uinT8) ((div2 + *sums++) / divisor);
      //turn to destination value
      div2 = (yindex * xfactor << (-shift)) / 2;
      for (xindex = source->bytespp; xindex > 0; xindex--)
        *pixel++ =
          (uinT8) ((div2 + *sums++) / (yindex * xfactor << (-shift)));
      //lastone different
    }
    else {
      div2 = divisor / 2;
      for (sums = linesums, xindex = (destext - 1) * source->bytespp;
        xindex > 0; xindex--)
      *pixel++ = (uinT8) ((div2 + (*sums++ << shift)) / divisor);
      //destination value
      div2 = (yindex * xfactor) / 2;
      for (xindex = source->bytespp; xindex > 0; xindex--)
        *pixel++ =
          (uinT8) ((div2 + (*sums++ << shift)) / (yindex * xfactor));
      //last one different
    }
                                 //put in destination
    dest->put_line (xdest, ydest, destext, &copyline, 0);
  }
  delete linesums;
}


/**********************************************************************
 * invert_image
 *
 * Invert the given image (the slow way.)
 **********************************************************************/

DLLSYM void invert_image(              /*invert the image */
                         IMAGE *image  /*image ot invert */
                        ) {
  uinT8 mask;                    //bit mask
  uinT8 bytespp;                 //bytes per pixel
  inT32 xsize, ysize;            /*size of image */
  inT32 xindex, yindex;          /*index into image */
  uinT8 *pixel;                  /*current pixel */
  IMAGELINE line;                /*line of image */

  bytespp = image->get_bpp () == 24 ? 3 : 1;
  xsize = image->get_xsize ();   /*find sizes */
  ysize = image->get_ysize ();
                                 //pixel mask
  mask = (1 << image->get_bpp ()) - 1;
                                 /*do each line */
  for (yindex = ysize - 1; yindex >= 0; yindex--) {
    image->fast_get_line (0, yindex, xsize, &line);
    for (pixel = line.pixels, xindex = xsize * bytespp; xindex > 0;
      xindex--) {
      *pixel = (*pixel) ^ mask;  //invert image only
      ++pixel;
    }
                                 /*put it back */
    image->fast_put_line (0, yindex, xsize, &line);
  }
}


/**********************************************************************
 * bias_sub_image
 *
 * Add a constant to a portion of an image.
 **********************************************************************/

DLLSYM void bias_sub_image(                //bias rectangle
                           IMAGE *source,  //source image
                           inT32 xstart,   //start coords
                           inT32 ystart,
                           inT32 xext,     //extent to copy
                           inT32 yext,
                           uinT8 bias      //number to add
                          ) {
  IMAGELINE copyline;            //copy of line
  uinT8 *copy;                   //source pointer
  inT32 pixel;                   //pixel index
  inT32 y;                       //line index
  uinT8 bytespp;                 //bytes per pixel

  if (xstart < 0 || ystart < 0)
    return;
  if (xext <= 0)
    xext = source->get_xsize (); //default to all
  if (xext > source->get_xsize () - xstart)
                                 //clip to smallest
    xext = source->get_xsize () - xstart;
  if (yext <= 0)
    yext = source->get_ysize (); //default to all
  if (yext > source->get_ysize () - ystart)
                                 //clip to smallest
    yext = source->get_ysize () - ystart;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

  bytespp = source->get_bpp () == 24 ? 3 : 1;
  for (y = 0; y < yext; y++) {
    source->check_legal_access (xstart, ystart + y, xext);
    source->fast_get_line (xstart, ystart + y, xext, &copyline);
    for (pixel = xext * bytespp, copy = copyline.pixels; pixel > 0;
      pixel--, copy++)
    *copy += bias;               //add bias

    source->fast_put_line (xstart, ystart + y, xext, &copyline);
  }
}


/**********************************************************************
 * starbase_to_normal
 *
 * Copy a portion of one image to a portion of another image.
 * This function maps the colour tables used on the screen to
 * greyscale values in the way "normally" expected.
 **********************************************************************/

DLLSYM void starbase_to_normal(                     //copy rectangle
                               IMAGE *source,       //source image
                               inT32 xstart,        //start coords
                               inT32 ystart,
                               inT32 xext,          //extent to copy
                               inT32 yext,
                               IMAGE *dest,         //destination image
                               inT32 xdest,         //destination coords
                               inT32 ydest,
                               BOOL8 preserve_grey  //shift to new bpp
                              ) {
  IMAGELINE copyline;            //copy of line
  uinT8 *copy;                   //source pointer
  inT8 shift4;                   //shift factor
  inT8 shift6;                   //shift factor
  inT8 colour_shift;             //shift of colours
  uinT8 white_level;             //dest white value
  inT32 pixel;                   //pixel index
  inT32 y;                       //line index
  inT32 yoffset;                 //current adjusted offset
  inT8 srcppb;                   //pixels per byte

  if (xstart < 0 || ystart < 0 || xdest < 0 || ydest < 0)
    return;
  if (xext <= 0)
    xext = source->get_xsize (); //default to all
  if (xext > source->get_xsize () - xstart)
                                 //clip to smallest
    xext = source->get_xsize () - xstart;
  if (xext > dest->get_xsize () - xdest)
    xext = dest->get_xsize () - xdest;
  if (yext <= 0)
    yext = source->get_ysize (); //default to all
  if (yext > source->get_ysize () - ystart)
                                 //clip to smallest
    yext = source->get_ysize () - ystart;
  if (yext > dest->get_ysize () - ydest)
    yext = dest->get_ysize () - ydest;
  if (xext <= 0 || yext <= 0)
    return;                      //nothing to do

                                 //pixels per byte
  srcppb = 8 / source->get_bpp ();
  shift4 = 4 - dest->get_bpp (); //for different bpps
  shift6 = 6 - dest->get_bpp ();
                                 //for grey preserve
  colour_shift = 8 - dest->get_bpp ();
  white_level = dest->get_white_level ();
  for (y = 0; y < yext; y++) {
    if (ystart >= ydest)
      yoffset = y;               //top down
    else
      yoffset = yext - y - 1;    //bottom up
    source->check_legal_access (xstart, ystart + yoffset, xext);
    dest->check_legal_access (xdest, ydest + yoffset, xext);
    source->get_line (xstart, ystart + yoffset, xext, &copyline, 0);
    for (pixel = 0, copy = copyline.pixels; pixel < xext; pixel++) {
      if (*copy < FIXED_COLOURS && preserve_grey)
        *copy = grey_scales[*copy] >> colour_shift;
      else if (*copy < FIXED_COLOURS) {
        if (*copy == BLACK_PIX)
          *copy = white_level;   //black->white
        else
          *copy = 0;             //others->black
      }
      else if (*copy >= MIN_4BIT && *copy < MAX_4BIT) {
        if (shift4 < 0)
          *copy = (*copy - MIN_4BIT) << (-shift4);
        else
          *copy = (*copy - MIN_4BIT) >> shift4;
      }
      else if (*copy >= MIN_6BIT && *copy < MAX_6BIT) {
        if (shift6 < 0)
          *copy = (*copy - MIN_6BIT) << (-shift6);
        else
          *copy = (*copy - MIN_6BIT) >> shift6;
      }
      else {
        *copy = white_level;     //white the rest
      }
      copy++;
    }
    dest->put_line (xdest, ydest + yoffset, xext, &copyline, 0);
  }
}


/**********************************************************************
 * fast_get_line
 *
 * Get a line of image into the supplied image line buffer.
 * The image is converted to 8bpp by simple assignment.
 * If the image is aleady 8 or 6bpp, no copy is done and a pointer
 * to the correct image section is put in the line buffer.
 **********************************************************************/

void IMAGE::fast_get_line(                    //get image line
                          inT32 x,            //coord to start at
                          inT32 y,            //line to get
                          inT32 width,        //no of pixels to get
                          IMAGELINE *linebuf  //line to copy to
                         ) {
  if (width > 0 && bpp > 4) {
    check_legal_access(x, y, width);
                                 //get pointer only
    linebuf->pixels = image + xdim * (ymax - 1 - y) + x * bytespp;
  }
  else
                                 //just copy it
    this->get_line (x, y, width, linebuf, 0);
  linebuf->bpp = bpp;
}


/**********************************************************************
 * get_line
 *
 * Get a line of image into the supplied image line buffer.
 * The image is converted to 8bpp by simple assignment.
 **********************************************************************/

void IMAGE::get_line(                     //get image line
                     inT32 x,             //coord to start at
                     inT32 y,             //line to get
                     inT32 width,         //no of pixels to get
                     IMAGELINE *linebuf,  //line to copy to
                     inT32 margins        //size of margins
                    ) {
  uinT8 *src;                    //source pointer
  uinT8 *dest;                   //destination pointer
  uinT8 *unpacksrc;              //unpacking pointer
  inT8 bit;                      //bit index
  inT8 pixperbyte;               //pixels per byte
  uinT8 white;                   //white colour
  inT32 pixel;                   //pixel index

                                 //test coords
  this->check_legal_access (x, y, width);
  if (width > xsize - x)
    width = xsize - x;           //clip to image
  width *= bytespp;
  linebuf->init (width + margins * bytespp * 2);
  linebuf->bpp = bpp;
                                 //start of line
  src = image + xdim * (ymax - 1 - y);
  dest = linebuf->line;          //destination line
  linebuf->pixels = dest;
  white = (1 << bpp) - 1;        //max value of pixel
  for (pixel = margins * bytespp; pixel > 0; pixel--) {
    *dest++ = white;             //margins are white
  }
  if (width > 0) {
    if (bpp > 4) {
      src += x;                  //offset
                                 //easy way
      memmove (dest, src, (unsigned) width);
    }
    else if (bpp == 4) {
      src += x / 2;              //offset on line
      if (x & 1) {
                                 //get coded nibble
        *dest++ = bpp4table[*src++][1];
        width--;
      }
      while (width >= 2) {
                                 //get coded bits
        unpacksrc = bpp4table[*src++];
        *dest++ = *unpacksrc++;
        *dest++ = *unpacksrc++;  //copy nibbles
        width -= 2;
      }
      if (width) {
                                 //get coded nibble
        *dest++ = bpp4table[*src++][0];
      }
    }
    else if (bpp == 2) {
      pixperbyte = 4;
      src += x / 4;              //offset on line
      bit = (inT8) (x % 4);      //offset in byte
      width += bit;
      while (width > 0) {        //until all done
        if (width < pixperbyte)
                                 //less on last byte
          pixperbyte = (inT8) width;
                                 //get coded bits
        unpacksrc = &bpp2table[*src++][bit];
        for (; bit < pixperbyte; bit++)
          *dest++ = *unpacksrc++;//copy bytes
        width -= pixperbyte;
        bit = 0;
      }
    }
    else {
      pixperbyte = 8;
      src += x / 8;              //offset on line
      bit = (inT8) (x % 8);      //offset in byte
      width += bit;
      while (width > 0) {        //until all done
        if (width < pixperbyte)
                                 //less on last byte
          pixperbyte = (inT8) width;
                                 //get coded bits
        unpacksrc = &bpp1table[*src++][bit];
        for (; bit < pixperbyte; bit++)
          *dest++ = *unpacksrc++;//copy bytes
        width -= pixperbyte;
        bit = 0;
      }
    }
  }
  for (pixel = margins * bytespp; pixel > 0; pixel--) {
    *dest++ = white;             //margins are white
  }
}


/**********************************************************************
 * get_column
 *
 * Get a column of image into the supplied image line buffer.
 * The image is converted to 8bpp by simple assignment.
 **********************************************************************/

void IMAGE::get_column(                     //get image column
                       inT32 x,             //coord to start at
                       inT32 y,             //line to get
                       inT32 height,        //no of pixels to get
                       IMAGELINE *linebuf,  //line to copy to
                       inT32 margins        //size of margins
                      ) {
  uinT8 *src;                    //source pointer
  uinT8 *dest;                   //destination pointer
  inT8 bit;                      //bit index
  inT8 pixperbyte;               //pixels per byte
  uinT8 white;                   //white colour
  inT32 pixel;                   //pixel index

                                 //test coords
  this->check_legal_access (x, y, 1);
                                 //test coords
  this->check_legal_access (x, y + height - 1, 1);
  if (height > ysize - y)
    height = ysize - y;          //clip to image
  linebuf->init (height * bytespp + margins * bytespp * 2);
                                 //start of line
  src = image + xdim * (ymax - 1 - y);
  dest = linebuf->line;          //destination line
  linebuf->pixels = dest;
  white = (1 << bpp) - 1;        //max value of pixel
  for (pixel = margins * bytespp; pixel > 0; pixel--) {
    *dest++ = white;             //margins are white
  }
  if (height > 0) {
    if (bpp == 24) {
      src += x * bytespp;        //offset
      for (; height > 0; --height) {
        *dest++ = *src;          //copy bytes
        *dest++ = *(src + 1);
        *dest++ = *(src + 2);
        src -= xdim;
      }
    }
    else if (bpp > 4) {
      src += x;
      for (; height > 0; --height) {
        *dest++ = *src;          //copy bytes
        src -= xdim;
      }
    }
    else if (bpp == 4) {
      src += x / 2;              //offset on line
      if (x & 1) {
        for (; height > 0; --height) {
                                 //get coded nibble
          *dest++ = bpp4table[*src][1];
          src -= xdim;
        }
      }
      else {
        for (; height > 0; --height) {
                                 //get coded nibble
          *dest++ = bpp4table[*src][0];
          src -= xdim;
        }
      }
    }
    else if (bpp == 2) {
      pixperbyte = 4;
      src += x / 4;              //offset on line
      bit = (inT8) (x % 4);      //offset in byte
      for (; height > 0; --height) {
                                 //get coded bits
        *dest++ = bpp2table[*src][bit];
        src -= xdim;
      }
    }
    else {
      pixperbyte = 8;
      src += x / 8;              //offset on line
      bit = (inT8) (x % 8);      //offset in byte
      for (; height > 0; --height) {
                                 //get coded bits
        *dest++ = bpp1table[*src][bit];
        src -= xdim;
      }
    }
  }
  for (pixel = margins * bytespp; pixel > 0; pixel--) {
    *dest++ = white;             //margins are white
  }
}


/**********************************************************************
 * fast_put_line
 *
 * Put a line buffer back into the image.
 * If the line buffer merely points back into the image, nothing is done.
 * Otherwise, put_line is used to copy the line back.
 **********************************************************************/

void IMAGE::fast_put_line(                    //put image line
                          inT32 x,            //coord to start at
                          inT32 y,            //line to get
                          inT32 width,        //no of pixels to put
                          IMAGELINE *linebuf  //line to copy to
                         ) {
  if (width > 0 && (bpp <= 4 || linebuf->pixels == linebuf->line))
                                 //just copy it
    put_line (x, y, width, linebuf, 0);
}


/**********************************************************************
 * put_line
 *
 * Put the supplied line buffer into the image.
 * The image is converted from 8bpp by simple assignment.
 **********************************************************************/

void IMAGE::put_line(                     //put image line
                     inT32 x,             //coord to start at
                     inT32 y,             //line to get
                     inT32 width,         //no of pixels to get
                     IMAGELINE *linebuf,  //line to copy to
                     inT32 margins        //margins in buffer
                    ) {
  uinT8 *src;                    //source pointer
  uinT8 *dest;                   //destination pointer
  inT8 bit;                      //bit index
  uinT8 pixel;                   //collected bits
  inT8 pixperbyte;               //pixels in a byte
  inT8 bytesperpix;              //in source

  this->check_legal_access (x, y, width);
  if (width > xsize - x)
    width = xsize - x;           //clip to image
  if (width <= 0)
    return;                      //nothing to do
                                 //source line
  src = linebuf->pixels + margins;
                                 //start of line
  dest = image + xdim * (ymax - 1 - y);

  if (linebuf->bpp == 24) {
    src++;
    bytesperpix = 3;
  }
  else
    bytesperpix = 1;
  if (bpp == 24 && linebuf->bpp == 24) {
    dest += x * bytespp;
    width *= bytespp;
    memmove (dest, src - 1, (unsigned) width);
  }
  else if (bpp == 24) {
    src--;
    dest += x * bytespp;
    while (width > 0) {
      pixel = *src++;
      *dest++ = pixel;
      *dest++ = pixel;
      *dest++ = pixel;
      width--;
    }
  }
  else if (bpp > 4) {
    dest += x;                   //offset
    if (linebuf->bpp == 24) {
      while (width > 0) {
        *dest++ = *src;
        src += 3;
        width--;
      }
    }
    else
                                 //easy way
      memmove (dest, src, (unsigned) width);
  }
  else if (bpp == 4) {
    dest += x / 2;               //offset on line
    if (x & 1) {
      *dest &= 0xf0;             //clean odd byte
      *dest++ |= *src & 0x0f;    //and copy it
      src += bytesperpix;
      width--;
    }
    while (width >= 2) {
      pixel = *src << 4;         //left pixel
      src += bytesperpix;
      pixel |= *src & 0x0f;      //right pixel
      src += bytesperpix;
      *dest++ = pixel;
      width -= 2;
    }
    if (width) {
      *dest &= 0x0f;             //clean odd byte
      *dest |= *src << 4;
    }
  }
  else if (bpp == 2) {
    pixperbyte = 4;
    dest += x / 4;               //offset on line
    bit = (inT8) (x % 4);        //offset in byte
    width += bit;
    pixel = *dest >> (8 - bit - bit);
    while (width >= 4) {         //until all done
      for (; bit < 4; bit++) {
        pixel <<= 2;             //make space for new one
        pixel |= *src & 3;
        src += bytesperpix;
      }
      *dest++ = pixel;           //new pixel
      width -= 4;
      bit = 0;
    }
    if (width > 0) {             //until all done
      for (bit = 0; bit < width; bit++) {
        pixel <<= 2;             //make space for new one
        pixel |= *src & 3;
        src += bytesperpix;
      }
      pixel <<= (8 - bit - bit); //shift rest
                                 //keep trainling bits
      pixel |= *dest & ((1 << (8 - bit - bit)) - 1);
      *dest++ = pixel;           //new pixel
    }
  }
  else {
    pixperbyte = 8;
    dest += x / 8;               //offset on line
    bit = (inT8) (x % 8);        //offset in byte
    width += bit;
    pixel = *dest >> (8 - bit);
    while (width >= 8) {         //until all done
      for (; bit < 8; bit++) {
        pixel <<= 1;             //make space for new one
        pixel |= *src & 1;
        src += bytesperpix;
      }
      *dest++ = pixel;           //new pixel
      width -= 8;
      bit = 0;
    }
    width -= bit;
    if (width > 0) {             //until all done
      while (width > 0) {
        pixel <<= 1;             //make space for new one
        pixel |= *src & 1;
        src += bytesperpix;
        bit++;
        width--;
      }
      pixel <<= (8 - bit);       //shift rest
                                 //keep trainling bits
      pixel |= *dest & ((1 << (8 - bit)) - 1);
      *dest++ = pixel;           //new pixel
    }
  }
}


/**********************************************************************
 * put_column
 *
 * Put the supplied column buffer into the image.
 * The image is converted from 8bpp by simple assignment.
 **********************************************************************/

void IMAGE::put_column(                     //put image column
                       inT32 x,             //coord to start at
                       inT32 y,             //line to get
                       inT32 height,        //no of pixels to get
                       IMAGELINE *linebuf,  //line to copy to
                       inT32 margins        //margins in buffer
                      ) {
  uinT8 *src;                    //source pointer
  uinT8 *dest;                   //destination pointer
  inT8 bit;                      //bit index
  uinT8 pixel;                   //collected bits
  inT8 bytesperpix;              //in source

  this->check_legal_access (x, y, 1);
  this->check_legal_access (x, y + height - 1, 1);
  if (height > ysize - y)
    height = ysize - y;          //clip to image
  if (height <= 0)
    return;                      //nothing to do
                                 //source line
  src = linebuf->pixels + margins;
                                 //start of line
  dest = image + xdim * (ymax - 1 - y);

  if (linebuf->bpp == 24) {
    src++;
    bytesperpix = 3;
  }
  else
    bytesperpix = 1;

  if (bpp == 24 && linebuf->bpp == 24) {
    dest += x * bytesperpix;
    src--;
    for (; height > 0; --height) {
      *dest = *src++;
      *(dest + 1) = *src++;
      *(dest + 2) = *src++;
      dest -= xdim;
    }
  }
  else if (bpp == 24) {
    src--;
    dest += x * bytesperpix;
    for (; height > 0; --height) {
      pixel = *src++;
      *dest = pixel;
      *(dest + 1) = pixel;
      *(dest + 2) = pixel;
      dest -= xdim;
    }
  }
  else if (bpp > 4) {
    dest += x;                   //offset
    for (; height > 0; --height) {
      *dest = *src;
      src += bytesperpix;
      dest -= xdim;
    }
  }
  else if (bpp == 4) {
    dest += x / 2;               //offset on line
    if (x & 1) {
      for (; height > 0; --height) {
        *dest &= 0xf0;           //clean odd byte
        *dest |= *src & 0x0f;    //and copy it
        src += bytesperpix;
        dest -= xdim;
      }
    }
    else {
      for (; height > 0; --height) {
        *dest &= 0x0f;           //clean odd byte
        *dest |= *src << 4;
        src += bytesperpix;
        dest -= xdim;
      }
    }
  }
  else if (bpp == 2) {
    dest += x / 4;               //offset on line
    bit = (inT8) (x % 4);        //offset in byte
    bit = 6 - bit - bit;         //bit shift
    pixel = ~(3 << bit);         //mask
    for (; height > 0; --height) {
                                 //change 2 bits
      *dest = (*dest & pixel) | ((*src & 3) << bit);
      src += bytesperpix;
      dest -= xdim;
    }
  }
  else {
    dest += x / 8;               //offset on line
    bit = (inT8) (x % 8);        //offset in byte
    bit = 7 - bit;
    pixel = ~(1 << bit);
    for (; height > 0; --height) {
                                 //change 1 bit
      *dest = (*dest & pixel) | ((*src & 1) << bit);
      src += bytesperpix;
      dest -= xdim;
    }
  }
}


/**********************************************************************
 * check_legal_access
 *
 * Check that x,y are within the bounds of the image.
 * Call bufread if necessary to get the image into memory.
 **********************************************************************/

void IMAGE::check_legal_access(            //check coords are legal
                               inT32 x,    //coords to check
                               inT32 y,
                               inT32 xext  //xextent
                              ) {
  if (x < 0 || x >= xsize || y < 0 || y >= ysize || x + xext > xsize)
    BADIMAGECOORDS.error ("IMAGE::check_legal_access",
      ABORT, "(%d+%d,%d)", x, xext, y);
  if (y >= ymax)
    BADIMAGESEEK.error ("IMAGE::check_legal_access", ABORT, "(%d,%d)", x, y);
  if (y < ymin)
    bufread(y);  //read some more
}

#ifdef HAVE_LIBLEPT
// ONLY available if you have Leptonica installed.
/**********************************************************************
 * ToPix
 *
 * Make a Pix from this image.
 **********************************************************************/
Pix* IMAGE::ToPix() {
  int width = this->get_xsize();
  int height = this->get_ysize();
  int bpp = this->get_bpp();
  Pix* pix = pixCreate(width, height, bpp == 24 ? 32 : bpp);
  uint32* data = pixGetData(pix);
  IMAGELINE line;
  if (bpp == 24) {
    line.init(width * 3);
    line.set_bpp(24);
  } else {
    line.init(width);
  }
  switch (bpp) {
  case 1:
    for (int y = height - 1 ; y >= 0; --y) {
      this->get_line(0, y, width, &line, 0);
      for (int x = 0; x < width; ++x) {
        if (line.pixels[x])
          CLEAR_DATA_BIT(data, x);
        else
          SET_DATA_BIT(data, x);
      }
      data += pixGetWpl(pix);
    }
    break;

  case 8:
    // Greyscale just copies the bytes in the right order.
    for (int y = height - 1 ; y >= 0; --y) {
      this->get_line(0, y, width, &line, 0);
      for (int x = 0; x < width; ++x)
        SET_DATA_BYTE(data, x, line.pixels[x]);
      data += pixGetWpl(pix);
    }
    break;

  case 24:
    // Put the colors in the correct places in the line buffer.
    for (int y = height - 1 ; y >= 0; --y) {
      this->get_line(0, y, width, &line, 0);
      for (int x = 0; x < width; ++x, ++data) {
        SET_DATA_BYTE(data, COLOR_RED, line[x][RED_PIX]);
        SET_DATA_BYTE(data, COLOR_GREEN, line[x][GREEN_PIX]);
        SET_DATA_BYTE(data, COLOR_BLUE, line[x][BLUE_PIX]);
      }
    }
    break;

  default:
    tprintf("Cannot convert image to Pix with bpp = %d\n", bpp);
  }
  return pix;
}

/**********************************************************************
 * FromPix
 *
 * Copy from the given Pix into this image.
 **********************************************************************/
void IMAGE::FromPix(const Pix* src_pix) {
  // Leptonica doesn't const its inputs, but we don't change the input.
  Pix* pix = const_cast<Pix*>(src_pix);
  Pix* destroy_this_pix = NULL;

  int depth = pixGetDepth(pix);
  if (depth > 1 && depth < 8) {
    // Convert funny depths to 8 bit.
    destroy_this_pix = pixConvertTo8(pix, false);
    pix = destroy_this_pix;
    depth = pixGetDepth(pix);
  }
  int width = pixGetWidth(pix);
  int height = pixGetHeight(pix);
  const uint32* data = pixGetData(pix);
  this->create(width, height, depth == 32 ? 24 : depth);
  // For each line in the image, fill the IMAGELINE class and put it into the
  // destination image. Note that Tesseract stores images with the
  // bottom at y=0 and 0 is always black in grey and binary.
  IMAGELINE line;
  if (depth == 32) {
    line.init(width * 3);
    line.set_bpp(24);
  } else {
    line.init(width);
  }
  switch (depth) {
  case 1:
    // Binary images just flip the data bit.
    for (int y = height - 1 ; y >= 0; --y) {
      for (int x = 0; x < width; ++x)
        line.pixels[x] = GET_DATA_BIT(data, x) ^ 1;
      this->put_line(0, y, width, &line, 0);
      data += pixGetWpl(pix);
    }
    break;

  case 8:
    // Greyscale just copies the bytes in the right order.
    for (int y = height - 1 ; y >= 0; --y) {
      for (int x = 0; x < width; ++x)
        line.pixels[x] = GET_DATA_BYTE(data, x);
      this->put_line(0, y, width, &line, 0);
      data += pixGetWpl(pix);
    }
    break;

  case 32:
    // Put the colors in the correct places in the line buffer.
    for (int y = height - 1 ; y >= 0; --y) {
      for (int x = 0; x < width; ++x, ++data) {
        line[x][RED_PIX] = GET_DATA_BYTE(data, COLOR_RED);
        line[x][GREEN_PIX] = GET_DATA_BYTE(data, COLOR_GREEN);
        line[x][BLUE_PIX] = GET_DATA_BYTE(data, COLOR_BLUE);
      }
      this->put_line(0, y, width, &line, 0);
    }
    break;

  default:
    tprintf("Cannot convert Pix to image with bpp = %d\n", depth);
  }
  if (destroy_this_pix != NULL)
    pixDestroy(&destroy_this_pix);
}
#endif  // HAVE_LIBLEPT

/*************************************************************************
 * convolver()
 *
 * Calls the specified function for each pixel in the image, passing in an m x n
 * window of the image, centred on the pixel.  The convolution function returns
 * a new value for the pixel, based on the window.
 *
 * At the edges of the image, the window is padded to white pixels.
 *************************************************************************/

void
IMAGE::convolver (               //Map fn over window
inT32 win_width,                 //Window width
inT32 win_height,                //Window height
void (*convolve) (               //Conv Function
uinT8 ** pixels,                 //Of window
uinT8 bytespp,                   //1 or 3 for colour
inT32 win_wd,                    //Window width
inT32 win_ht,                    //Window height
uinT8 ret_white_value,           //White value to RETURN
uinT8 * result)                  //Ptr to result pix
) {
  IMAGELINE new_row;             //Replacement pixels
  IMAGELINE *old_rows;           //Rows being processed
  inT32 oldest_imline;           //Next imline to replace
  uinT8 **window;                //ptrs to pixel rows
  uinT8 **winmax;                //ptrs to pixel rows
  uinT8 **win;                   //ptrs to pixel rows
  inT32 current_row;             //Row being calculated
  inT32 current_col;             //Col being calculated
  inT32 row = 0;                 //Next row to get

  inT32 i, j;
  uinT8 *pix;
  uinT8 *max;
  inT32 xmargin = win_width / 2;
  inT32 ymargin = win_height / 2;
  uinT8 white = get_white_level ();
  const uinT8 max_white = 255;
  float white_scale = (float) 255 / get_white_level ();

  if (((win_width % 2) == 0) ||
    ((win_height % 2) == 0) ||
    (win_height < 3) ||
    (win_width < 3) || (win_height > ysize / 2) || (win_width > xsize / 2))
    BADWINDOW.error ("IMAGE::convolver",
      ABORT, "(%d x %d)", win_width, win_height);

  new_row.init (xsize * bytespp);
  new_row.set_bpp (bpp);
  old_rows = new IMAGELINE[win_height];
  for (i = 0; i < win_height; i++) {
    old_rows[i].init ((xsize + 2 * xmargin) * bytespp);
    old_rows[i].set_bpp (bpp);
  }

  window = (uinT8 **) alloc_mem (win_height * sizeof (uinT8 *));
  winmax = window + win_height;

  /* Make bottom border */
  for (oldest_imline = 0; oldest_imline < ymargin; oldest_imline++) {
    pix = old_rows[oldest_imline].pixels;
    max = pix + (xsize + 2 * xmargin) * bytespp;
    while (pix < max)
      *pix++ = max_white;
  }
  /* Initialise remaining rows but one*/
  for (; oldest_imline < win_height - 1; oldest_imline++) {
    get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin);
    if (max_white != white) {
      pix = old_rows[oldest_imline].pixels;
      max = pix + (xsize + 2 * xmargin) * bytespp;
      while (pix < max) {
        *pix = (uinT8) (*pix * white_scale);
        ++pix;
      }
    }
  }

  /* Image Processing */

  for (current_row = 0; current_row < ysize;) {
    /* Get next row and re-initialise window array */
    if (row < ysize) {
      get_line (0, row++, xsize, &old_rows[oldest_imline], xmargin);
      if (max_white != white) {
        pix = old_rows[oldest_imline].pixels;
        max = pix + (xsize + 2 * xmargin) * bytespp;
        while (pix < max) {
          *pix = (uinT8) (*pix * white_scale);
          ++pix;
        }
      }
    }
    else {
      pix = old_rows[oldest_imline].pixels;
      max = pix + (xsize + 2 * xmargin) * bytespp;
      while (pix < max)
        *pix++ = max_white;
    }
    oldest_imline++;
    if (oldest_imline >= win_height)
      oldest_imline = 0;

    /* Process line */
    pix = new_row.pixels;
    for (current_col = 0; current_col < xsize;) {
      /* Set up window ptrs */
      if (current_col == 0) {
        j = oldest_imline;
        for (i = 0; i < win_height; i++) {
          window[i] = old_rows[j++].pixels;
          if (j >= win_height)
            j = 0;
        }
      }
      else {
        for (win = window; win < winmax; (*win++) += bytespp);
        //Move along rows
      }

      convolve(window, bytespp, win_width, win_height, white, pix);
      pix += bytespp;
      current_col++;
    }

    put_line (0, current_row, xsize, &new_row, 0);
    new_row.init ();
    new_row.set_bpp (bpp);
    current_row++;
  }
}


//Fuad Sep 16, 2008
/************************************************************************* 
* Connected_Components(IMAGE* source, int& no_of_cmps, const char* fileName)
*
* Recieves an 8-bpp black-on-white IMAGE to be colored, each connected
* component with color code from 1 -- 2^bpp-2 and the number of components
* is stored in the second paramter.
* The function returns a pointer to the data structure containing the
* connected components information.
*
* connCmpInf is the struct name declared in the file "img.h" to contain the 
* needed information for each connected component
*
* code 0 reserved for BLACK,
* code 2^bpp-1 reserved for WHITE
*
* For 8-bpp: 0->BLACK , 255->White , 1 -- 254 connected components codes.
* An 8-bpp IMAGE is capable of identifying 254 connected components.
* 
*************************************************************************/
DLLSYM connCmpInf* Connected_Components(IMAGE* source, int& no_of_cmps, const char* fileName)
{
	DisjointSets<BYTE>					relableSets;
	std::list<BYTE>						uniqueColors;
	std::list<BYTE>::iterator			uniqueColors_it;
	BYTE	minN;
	BYTE	tmpByte;
	int		frequency[256];//max number of labels, limited by the fact that the image is 8 bit/pixel
	for(int i = 0; i < 256; i++) {
		frequency[i] = 0;
	}

	int x_size = source->get_xsize();
	int y_size = source->get_ysize();

	IMAGELINE line;
	line.init();

	IMAGE temp_img;
	
	BYTE WHITE = source->get_white_level();	//WHITE and BLACK data type should match the bpp of the IMAGE
	BYTE BLACK = !WHITE;

	int xt, yt, xt2, yt2;				//Pixels Traversers

	BYTE counter = 1;					//Counter is the code to be given to each connected component


	BYTE P1;							//P stand for pixel in the Previous row.
	BYTE P2;							//		P1  P2  P3
	BYTE P3;							//		L   *				where * denotes the pixel being analyzed
	BYTE L ;							//L stand for pixel to the left. 
	
	//Initial Labeling:
	for(yt = y_size - 2; yt >= 0; yt--) {
		for(xt = 1; xt < x_size; xt++) {
			if(source->pixel(xt,yt) == BLACK)	{
				P1 = source->pixel(xt-1,yt+1);
				P2 = source->pixel( xt ,yt+1);
				P3 = source->pixel(xt+1,yt+1);
				L  = source->pixel(xt-1,yt);

				if(L == WHITE &&
				P1 == WHITE &&
				P2 == WHITE &&
				P3 == WHITE) {
					relableSets.makeSet(counter);
					line.pixels[0] = counter++;
					source->put_column(xt, yt, 1, &line, 0);
				}
				else {
					minN = WHITE;
					if(L  != BLACK)					minN = L;
					if(P1 != BLACK && P1 < minN)	minN = P1;
					if(P2 != BLACK && P2 < minN)	minN = P2;
					if(P3 != BLACK && P3 < minN)	minN = P3;
				
					line.pixels[0] = minN;
					source->put_column(xt, yt, 1, &line, 0);
					if(L  != WHITE && L  != minN)	relableSets.unionSet(L, minN);
					if(P1 != WHITE && P1 != minN)	relableSets.unionSet(P1, minN);
					if(P2 != WHITE && P2 != minN)	relableSets.unionSet(P2, minN);
					if(P3 != WHITE && P3 != minN)	relableSets.unionSet(P3, minN);
				}
			}
		}
	}
	//source->write("beforeUnification.tif");

	relableSets.representativesList(&uniqueColors);
	uniqueColors.sort();
	uniqueColors.unique();
	counter = 1;
	std::map<BYTE, BYTE>	representativeToSequentialLabel;

	for(std::list<BYTE>::iterator it = uniqueColors.begin(); it != uniqueColors.end(); it++) {
		representativeToSequentialLabel[*it] = counter++;
	}

	//Label_Unification
	for(yt = y_size-2; yt >= 0; yt--) {
		for(xt = 1; xt < x_size; xt++) {
			if((tmpByte = source->pixel(xt,yt)) != WHITE) {
				tmpByte = representativeToSequentialLabel[relableSets.findSet(tmpByte)];
				line.pixels[0] = tmpByte;
				source->put_column(xt, yt, 1, &line, 0);
				frequency[tmpByte]++;
			}
		}
	}
	//source->write("afterUnification.tif");




	//Creating the data structure to store the connected components information:
	connCmpInf* connCmpPtr;
	//connCmpPtr = (connCmpInf*)malloc(sizeof(connCmpInf) * counter);
	connCmpPtr = new connCmpInf[counter];



	//The outer-most loop is to traverse each connected component, components codes
	//start from 1 and ends at counter-1 :
	int i , j;
	for(int cnt = 1; cnt < counter; cnt++) {
		//Storing the component information
		connCmpPtr[cnt].pixelsCnt = frequency[cnt];

		//Loops to find the corners of the box containing the connected components:
		//Loop to find x1:
		i=0;	j=0;
		while( (i < x_size) && (source->pixel(i++,j-1) != cnt) ) {
			j=0;
			while( (j < y_size) && (source->pixel(i,j++) != cnt) );
		}
		connCmpPtr[cnt].x1 = i-1;

		//Loop to find y1:
		i = connCmpPtr[cnt].x1 ;		j=0;
		while( (j < y_size) && (source->pixel(i-1,j++) != cnt) ) {
			i = connCmpPtr[cnt].x1;
			while( (i < x_size) && (source->pixel(i++,j) != cnt) );
		}
		connCmpPtr[cnt].y1 = j-1;

		//Loop to find x2:
		i = x_size-1;		j = y_size-1;
		while( (i >= connCmpPtr[cnt].x1) && (source->pixel(i--,j+1) != cnt) ) {
			j = y_size-1;
			while( (j >= connCmpPtr[cnt].y1) && (source->pixel(i,j--) != cnt) );
		}
		connCmpPtr[cnt].x2 = i+1;

		//Loop to find y2:
		i = connCmpPtr[cnt].x2;		j = y_size-1;
		while( (j >= connCmpPtr[cnt].y1) && (source->pixel(i+1,j--) != cnt) ) {
			i = connCmpPtr[cnt].x2;
			while( (i >= connCmpPtr[cnt].x1) && (source->pixel(i--,j) != cnt) );
		}
		connCmpPtr[cnt].y2 = j+1;

		//Width and height calculations:
		connCmpPtr[cnt].width = connCmpPtr[cnt].x2 - connCmpPtr[cnt].x1 + 1;
		connCmpPtr[cnt].height = connCmpPtr[cnt].y2 - connCmpPtr[cnt].y1 + 1;

		//Area calculation:
		connCmpPtr[cnt].area = connCmpPtr[cnt].width * connCmpPtr[cnt].height;

		//Density calculation:
		connCmpPtr[cnt].density = (double)connCmpPtr[cnt].pixelsCnt / connCmpPtr[cnt].area;
	}

	//Retururning the outputs to the caller function:
	no_of_cmps = counter - 1;	//Storing the number of components
	
	return connCmpPtr;
}//End of Connected_Components



/************************************************************************* 
* Identify_Seconderies(IMAGE* source, int no_of_cmps, connCmpInf* connCmpPtr)
*
* Recieves an 8-bpp black-on-white IMAGE, it identifies seconderies by setting the value of
* the flag is_secondery in the structure connCmpInf[component code] to 1 if the component is secondery
* 
*************************************************************************/
DLLSYM void Identify_Seconderies(IMAGE* source, int no_of_cmps, connCmpInf* connCmpPtr)
{
	//Defining connected components properties:   << THE COUNTER IN THE LOOP SHOULD BE 'i' >>
#define X1				(connCmpPtr[i].x1)
#define X2				(connCmpPtr[i].x2)
#define Y1				(connCmpPtr[i].y1)
#define Y2				(connCmpPtr[i].y2)
#define P				(connCmpPtr[i].pixelsCnt)
#define W				(connCmpPtr[i].width)
#define H				(connCmpPtr[i].height)
#define Area			(connCmpPtr[i].area)
#define Density			(connCmpPtr[i].density)
#define LONG			(image_y2 - image_y1)
#define ALEF			( H >= LONG/3.5 && W <= H/1.6 )
#define DIST			( (P-100) / 25 )
#define LOW_PIXELS		600
#define DOT				100
//#define HAMZA			( (H >= 0.8*W && H <= 1.2*W) && density >= 0.3 && P <= LOW_PIXELS )

			
	//Variables:

	/////////////////////////////////////////////////////////////////////////////
	//Preparing string from file name to use it in writing the image to the disk
	const char* fileName = strrchr(__argv[1], '\\');
	fileName++;

	char* sec_bl_FileName = new char[50];
	strcpy(sec_bl_FileName, "");
	strcat(sec_bl_FileName, "Sec_BL_");
	strcat(sec_bl_FileName, fileName);
	////

	IMAGELINE line;
	line.init();

	BYTE WHITE = source->get_white_level();

	//int bl = Find_BaseLine(source);
	int bl0, blhalf, bl1, bl2;
	int image_x1, image_x2, image_y1, image_y2;		//To hold the corners of the whole IMAGE
	int x_size = source->get_xsize();
	int y_size = source->get_ysize();
	//int BL;											//BL is the modified base line
	int i = 0;										//'i' should be used to traverse connected components
													//because we used it in the preprocessor definations

	bool got_below = false;							//To check if alef belongs to 6a'2 or not
	bool got_right = false;							//To check alef if it has ra2 to right
	int dist;										//To hold the distance between the alef and the component below it
	
	//The code below is to store the coordinates of the corners of the smallest box containing
	//the IMAGE, we will need them for modifying the base line value
	//Loop to find x1:
	int k , j;
	k=0;	j=0;
	while( (k < x_size) && (source->pixel(k++,j-1) == WHITE) ) {
		j=0;
		while( (j < y_size) && (source->pixel(k,j++) == WHITE) );
	}
	image_x1 = k-1;
	//Loop to find y1:
	k = image_x1 ;		j=0;
	while( (j < y_size) && (source->pixel(k-1,j++) == WHITE) ) {
		k = image_x1;
		while( (k < x_size) && (source->pixel(k++,j) == WHITE) );
	}
	image_y1 = j-1;
	//Loop to find x2:
	k = x_size-1;		j = y_size-1;
	while( (k >= image_x1) && (source->pixel(k--,j+1) == WHITE) ) {
		j = y_size-1;
		while( (j >= image_y1) && (source->pixel(k,j--) == WHITE) );
	}
	image_x2 = k+1;
	//Loop to find y2:
	k = image_x2;		j = y_size-1;
	while( (j >= image_y1) && (source->pixel(k+1,j--) == WHITE) ) {
		k = image_x2;
		while( (k >= image_x1) && (source->pixel(k--,j) == WHITE) );
	}
	image_y2 = j+1;


	//Base line handling:
	Find_All_Baselines(source, bl0, blhalf, bl1, bl2);
	//n: above variables weren't initalized, a value will be assigned to them in the function

	//If we use the original version of Find_AllBaseLine then yhalf should be:
	//blhalf = blhalf + (y_size - 40)/4 + 20;

	//blhalf in Find_AllBaselines finds the baseline wrt ground of half the image starting from h/4,
	//we need to add h/4 to make it wrt ground in the source image. Refer to Find_All_Baselines for more
	//information
	//blhalf += (y_size/4);	


	double m = (double)(bl2 - bl1)/(image_x2 - image_x1);			//Variable to hold the slope of the baseline
																	//to be used if bl1 != bl2

#define BL ( bl1 != bl2 ? (m * ((X1 - image_x1 + X2 - image_x1)/2) + bl1) : (blhalf >= 2*y_size/3 ? bl0 : blhalf) )

	//BL = abs(bl0 - image_y1) < 10 ? bl0 + 28 : bl0;		
	//int fff = BL;//test
		
	//Identifying the seconderies:
#define is_sec_1  ( (P <= LOW_PIXELS) && ( ((Y1 - BL) >= (DIST + 5)) || ((BL - Y2) >= DIST + (P > 275 ? 5 : 0)) ) ) 
	
	for(i = 1; i <= no_of_cmps; i++) {
		if( (P < DOT) || is_sec_1 )
			connCmpPtr[i].is_secondery = true;

		got_below = false;
		got_right = false;
		if( ALEF ) {		//Check alef, if no components below it then it is not a secondery
			for(int s = Y1; s >= 0; s--)		//Loop to check if the alef has close component below it (eg.6areq)
				if(source->pixel(X2 + 7, s) != WHITE) {
					dist = Y1 - s + 1;
					got_below = dist < 13;
					break;
				}
			for(int s = X2; s < x_size; s++)		//Loop to check if the alef has close component to its right (eg.rami)
				if(source->pixel(s, Y1 + 15) != WHITE) {
					dist = s - X2 + 1;
					got_right = dist < 25;
					break;
				}
			if( !got_below || got_right ) connCmpPtr[i].is_secondery = false;
		}

	}

	//Copying the source image to a new temp image to modify for testing purposes
	/*IMAGE tmpImg;
	tmpImg.create(x_size, y_size, 8);
	copy_sub_image(source, 0, 0, x_size, y_size, &tmpImg, 0, 0, 1);

	//Coloring seconderies with distiguished color for testing purposes:
	for(int toColor = 1; toColor <= no_of_cmps; toColor++) { 
		if(connCmpPtr[toColor].is_secondery == true) {
			for(int k = 0; k < y_size; k++)
				for(int j = 0; j < x_size; j++)
					if( source->pixel(j,k) == toColor) {//n: what?
						tmpImg.get_line(j, k, 1, &line, 0);
						line.pixels[0] = 150;
						tmpImg.put_line(j, k, 1, &line, 0);
					}
		}
	}
		
	//Marking the Modified base line for testing purposes:
	if( bl1 != bl2 ) {
		for(int yt = 0; yt < y_size; yt++)
			for(i = 0; i < x_size; i++)
				if( yt == (int)(m * ( i - image_x1 ) + bl1) ) {
					tmpImg.get_line(i, yt, 1, &line, 0);
					line.pixels[0] = 210;
					tmpImg.put_line(i, yt, 1, &line, 0);
				}
	}
	else {
		for(int s = 0; s < x_size; s++)
		line.pixels[s] = 210;
	tmpImg.put_line(0,BL,x_size,&line, 0);
	}
	
	tmpImg.write(sec_bl_FileName);*/

	//Ahmad::
	//Nadine:
	//Seconderies that belong to the same letter are given the same groupSecondery value
	int distance = (y_size - 40)/4;//allowed distance of secondery from another secondery on the same letter
	int belongs_to = 0;//counter
	for(int i = 1; i <= no_of_cmps; i++) 
	{
		if( connCmpPtr[i].is_secondery )
		{
			belongs_to++;//increments for each secondery
			for(int j = 1; j <= no_of_cmps; j++) 
			{
				//the secondery should be less than or equal to half the size of the secondery it is grouped with
				//i.e. this if statement only aims to group dots together
				//similar seconderies are searched for to the right, left, directly above and below the current secondery but not
				//along the diagonal
				if( j != i && connCmpPtr[j].is_secondery && ((connCmpPtr[j].pixelsCnt < connCmpPtr[i].pixelsCnt * 2) && (connCmpPtr[j].pixelsCnt * 2 > connCmpPtr[i].pixelsCnt)) &&
					((connCmpPtr[j].x1 < connCmpPtr[i].x2 + distance && connCmpPtr[j].x1 > connCmpPtr[i].x1) && ((connCmpPtr[j].y1 <= connCmpPtr[i].y2) && (connCmpPtr[j].y2 >= connCmpPtr[i].y1)) || //check right
					 (connCmpPtr[j].x2 > connCmpPtr[i].x1 - distance && connCmpPtr[j].x2 < connCmpPtr[i].x2) && ((connCmpPtr[j].y1 <= connCmpPtr[i].y2) && (connCmpPtr[j].y2 >= connCmpPtr[i].y1)) || //check left
					 (connCmpPtr[j].y1 < connCmpPtr[i].y2 + distance && connCmpPtr[j].y1 > connCmpPtr[i].y1) && ((connCmpPtr[j].x1 <= connCmpPtr[i].x2) && (connCmpPtr[j].x2 >= connCmpPtr[i].x1)) || //check above
					 (connCmpPtr[j].y2 > connCmpPtr[i].y1 - distance && connCmpPtr[j].y2 < connCmpPtr[i].y2) && ((connCmpPtr[j].x1 <= connCmpPtr[i].x2) && (connCmpPtr[j].x2 >= connCmpPtr[i].x1)))   //check below
					)
				{
					//similar seconderies are given same groupSecondery value
					if ( connCmpPtr[i].groupSecondery == 0)
						connCmpPtr[i].groupSecondery = belongs_to;//given the number in the counter
					if ( connCmpPtr[j].groupSecondery == 0)
						connCmpPtr[j].groupSecondery = connCmpPtr[i].groupSecondery;
					else
						if (connCmpPtr[j].groupSecondery != connCmpPtr[i].groupSecondery)
							connCmpPtr[i].groupSecondery = connCmpPtr[j].groupSecondery;
					
				}
				
			}
		}
	}

	//if all of secondery1 is above secondery2, and secondery2 has a larger width than secondery1, it is most likely
	//the letter sheen and the two seconderies should be grouped together
	for (int i = 0; i <= no_of_cmps; i++)
	{
		if ((connCmpPtr[i].is_secondery == 1) && (connCmpPtr[i].groupSecondery == 0))
		{ 
			belongs_to++;
			for (int j = 0; j <= no_of_cmps; j++)
			{
				if ( (i != j) && (connCmpPtr[j].is_secondery == 1) && (connCmpPtr[j].groupSecondery == 0) && (connCmpPtr[i].x1 > connCmpPtr[j].x1) && (connCmpPtr[i].x2 < connCmpPtr[j].x2) && (connCmpPtr[i].y2 > connCmpPtr[j].y2))
				{
					connCmpPtr[i].groupSecondery = belongs_to;
					connCmpPtr[j].groupSecondery = belongs_to;
				}
			}
		}
	}	
#undef X1
#undef X2
#undef Y1
#undef Y2
#undef P
#undef W
#undef H
#undef Area
#undef Density
#undef LONG
#undef ALEF
#undef DIST
#undef LOW_PIXELS
#undef DOT
}//End of Identify_Seconderies

/************************************************************************* 
* Assign_Seconderies
*
* Recieves an 8-bpp black-on-white IMAGE, pointer to the data structure containing
* the connected components information, a reference pointer to BYTE array (inc_x1_codes),
* and a reference pointer to BYTE array (dec_x2_codes).
*
* The BYTE array inc_x1_codes will contain the color codes of the components in an increasing
* order wrt x1 of the component, it will be used to traverse the components from left to right.
* e.g.)connCmpPtr[inc_x1_codes[1]]  is the leftmost component.
* The BYTE array dec_x2_codes is the dual of inc_x1_codes.
* 
*************************************************************************/
DLLSYM void Assign_Seconderies(IMAGE* source, int no_of_cmps, connCmpInf* connCmpPtr, BYTE* &inc_x1_codes, BYTE* &dec_x2_codes )
{
	//Defining connected components properties:   << THE COUNTER IN THE LOOP SHOULD BE 'i' >>
#define X1				(connCmpPtr[inc_x1_codes[i]].x1)
#define X2				(connCmpPtr[inc_x1_codes[i]].x2)
#define Y1				(connCmpPtr[inc_x1_codes[i]].y1)
#define Y2				(connCmpPtr[inc_x1_codes[i]].y2)
#define IS_SEC			(connCmpPtr[inc_x1_codes[i]].is_secondery)
#define GROUP_SEC		(connCmpPtr[inc_x1_codes[i]].groupSecondery)
#define BELONGS_TO		(connCmpPtr[inc_x1_codes[i]].belongs_to)

	//Variables definitions:
	int x_size = source->get_xsize();
	int y_size = source->get_ysize();
	int i = 0;										//For traversing the connected components
	int j = 0;

	BYTE WHITE = source->get_white_level();

	IMAGELINE line;
	line.init();

	//Variables needed for ordering the connected components based on their x values.
	//We need to traverse the components in two ways: from left to right -> increasing x1
	//												: from right to left -> decreasing x2
	
	int *inc_x1_x1s , *dec_x2_x2s;
	inc_x1_codes = new BYTE[no_of_cmps+1];
	dec_x2_codes = new BYTE[no_of_cmps+1];
	inc_x1_x1s = new int[no_of_cmps+1];
	dec_x2_x2s = new int[no_of_cmps+1];
	BYTE tmpColor;
	int tmpX;

	//Initializing the arrays of colors' codes and the arrays of x1s and x2s
	for( int k = 1; k <= no_of_cmps; k++ ) {	
		inc_x1_codes[k] = k;
		inc_x1_x1s[k] = connCmpPtr[k].x1;
		dec_x2_codes[k] = k;
		dec_x2_x2s[k] = connCmpPtr[k].x2;
	}

	//Storing the codes of the components with increasing x1 in the array inc_x1_codes.
	//After executing this loop: inc_x1_codes[1] is the CODE of the leftmost component.
	//							 inc_x1_codes[no_of_cmps] is the CODE of the rightmost component.
	for( i = 1; i < no_of_cmps; i++ ) {
		for( j = 1; j < no_of_cmps; j++ ) 
			if( inc_x1_x1s[j] > inc_x1_x1s[j+1] ) {		//Swapping
				tmpColor = inc_x1_codes[j];
				inc_x1_codes[j] = inc_x1_codes[j+1];
				inc_x1_codes[j+1] = tmpColor;

				tmpX = inc_x1_x1s[j];
				inc_x1_x1s[j] = inc_x1_x1s[j+1];
				inc_x1_x1s[j+1] = tmpX;
			}
	}
	
	//Storing the codes of the components with decreasing x2 in the array dec_x2_codes.
	//After executing this loop: dec_x2_codes[1] is the CODE of the rightmost component.
	//							 dec_x2_codes[no_of_cmps] is the CODE of the leftmost component.
	for( i = 1; i < no_of_cmps; i++ ) {
		for( j = 1; j < no_of_cmps; j++ ) 
			if( dec_x2_x2s[j] < dec_x2_x2s[j+1] ) {		//Swapping
				tmpColor = dec_x2_codes[j];
				dec_x2_codes[j] = dec_x2_codes[j+1];
				dec_x2_codes[j+1] = tmpColor;
								
				tmpX = dec_x2_x2s[j];
				dec_x2_x2s[j] = dec_x2_x2s[j+1];
				dec_x2_x2s[j+1] = tmpX;
			}
	}

	//Assigning seconderies to main bodies
	int minX1, maxX2, minY1, maxY2;
	int image_x2 = connCmpPtr[dec_x2_codes[1]].x2;
	for( i = 1; i <= no_of_cmps; i++ ) {
		if( IS_SEC == true ) {

			minX1 = X1;
			maxX2 = X2;
			minY1 = Y1;
			maxY2 = Y2;
			if( GROUP_SEC != 0 )
			{
				
				for(int j = 1; j <= no_of_cmps; j++)
					if( j != i && connCmpPtr[inc_x1_codes[j]].groupSecondery == GROUP_SEC)
					//if more than one secondery has the same groupSecondery value, they will be considered as one secondery
					//for the rest of this function.
					//minX1 = x1 of the secondery with the smallest x1 value, and so on.
					{
						minX1 = min(minX1 , connCmpPtr[inc_x1_codes[j]].x1);
						maxX2 = max(maxX2 , connCmpPtr[inc_x1_codes[j]].x2);
						minY1 = min(minY1 , connCmpPtr[inc_x1_codes[j]].y1);
						maxY2 = max(maxY2 , connCmpPtr[inc_x1_codes[j]].y2);
						
					}
			}

			//check if secondery belongs to the letter kaf
			if ((BELONGS_TO == 0) && (maxY2 > (y_size - y_size/4)) && ((maxY2 - minY1) > y_size/6))//make sure Y2 is in the top
																								  //top quarter of the image. And check secondery's size
			{
				for (int y1t = minY1; (y1t <= minY1 + 3) && (BELONGS_TO == 0) ; y1t++)
				if((source->pixel(minX1, y1t) == inc_x1_codes[i]) && (source->pixel( minX1, y1t ) != WHITE))//make sure minX1 and minY1 are in the same vicinity
																											//otherwise secondery might not belong to kaf
				{
					for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )//associate secondery with main body under its left endpoint (under X1)		
					if( (source->pixel(minX1, yt) != WHITE) && (connCmpPtr[source->pixel(minX1, yt)].is_secondery == false) )
					{	
						BELONGS_TO = source->pixel(minX1, yt);
			
					}
				}
			}
			int midx = (minX1 + maxX2)/2;
			for( int yt = minY1; (yt < y_size) && (BELONGS_TO == 0); yt++ )	//Check if main body above midpoint
				if( (source->pixel(midx, yt) != WHITE) && (connCmpPtr[source->pixel(midx, yt)].is_secondery == false) )
					BELONGS_TO = source->pixel(midx, yt);
	
			for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )		//Check if main body below midpoint
				if( (source->pixel(midx, yt) != WHITE) && (connCmpPtr[source->pixel(midx, yt)].is_secondery == false) )
				{	BELONGS_TO = source->pixel(midx, yt);
			break;
			}
			for( int yt = minY1; (yt < y_size) && (BELONGS_TO == 0); yt++ )	//Check if main body above left endpoint
				if( (source->pixel(minX1, yt) != WHITE) && (connCmpPtr[source->pixel(minX1, yt)].is_secondery == false) )
					BELONGS_TO = source->pixel(minX1, yt);

			for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )		//Check if main body below left endpoint
				if( (source->pixel(minX1, yt) != WHITE) && (connCmpPtr[source->pixel(minX1, yt)].is_secondery == false) )
					BELONGS_TO = source->pixel(minX1, yt);

			for( int xt = minX1 + 1; (xt < image_x2) && (BELONGS_TO == 0); xt++ )	//Check for main body up then down then to the right 
				for( int yt = y_size - 1; (yt >= 0) && (BELONGS_TO == 0); yt-- ) 
					if( (source->pixel(xt, yt) != WHITE) && (connCmpPtr[source->pixel(xt, yt)].is_secondery == false) )
						BELONGS_TO = source->pixel(xt, yt);
					
			if( BELONGS_TO == 0 )						//If no main body above nor below nor to right the secondery
				for( int j = 1; j <= no_of_cmps; j++ )  //then it's the rightmost unattached secondery; attach to rightmost main body
					if( connCmpPtr[dec_x2_codes[j]].is_secondery == false ) {
						BELONGS_TO = dec_x2_codes[j];
						break;
					}
		}
	}

	
	delete []inc_x1_x1s;
	delete []dec_x2_x2s;

#undef X1				
#undef X2				
#undef Y1			
#undef Y2			
#undef IS_SEC
#undef GROUP_SEC
#undef BELONGS_TO				
}//End of Assign_Seconderies


/************************************************************************* 
* Extract_Subwords
*
* This function extracts subwors in the IMAGE, the distance between the righmost subword
* and the right edge of the IMAGE is defined by MARGIN. The distance between subwords is defined
* by SPACE.
* 
* This function requires the array dec_x2_codes to be filled by the function Assign_Secondries, so that
* funtion should be called before this funciton.
*
* This function returns a pointer to a data structure that contains the corner coordinats of each subword
* in the IMAGE.
* 
*************************************************************************/
DLLSYM subword* Extract_Subwords(IMAGE* source,
								 int no_of_cmps,
								 connCmpInf* connCmpPtr,
								 BYTE* &inc_x1_codes,
								 BYTE* &dec_x2_codes,
								 int& no_of_subs)
{
#define SPACE 10		//Space between subwords
#define MARGIN 5		//Space between rightmost subword and the right edge

	//Writing the IMAGE before extracting to a file for testing purposes
	//source->write("before_extracting.tif");

	//Variables:
	/////////////////////////////////////////////////////////////////////////////
	//Preparing string from file name to use it in writing the image to the disk
	const char* fileName = strrchr(__argv[1], '\\');
	fileName++;

	char* ExtSubs_FileName = new char[50];
	strcpy(ExtSubs_FileName, "");
	strcat(ExtSubs_FileName, "ExtSubs_");
	strcat(ExtSubs_FileName, fileName);
	////
	BYTE WHITE = source->get_white_level();
	int no_of_subwords = 0;
	int s = 1;											//To traverse the subwords							
	subword* subwordPtr;
	int x_size = source->get_xsize();
	int y_size = source->get_ysize();

	IMAGELINE line;
	line.init();
	
	//Finding the number of subwords = number of main bodies
	for( int i = 1; i <= no_of_cmps; i++ )
		if( connCmpPtr[i].is_secondery == false ) no_of_subwords++;

	//Allocating a data structure to hold subwords info
	subwordPtr = new subword[no_of_subwords + 1];			//+1 to index subwords from 1

	//Filling the information of the subwords data structure
	for( int i = 1; i <= no_of_cmps; i++ ) {
		if( connCmpPtr[dec_x2_codes[i]].is_secondery == false ) {
			subwordPtr[s].x1 = connCmpPtr[dec_x2_codes[i]].x1;
			subwordPtr[s].x2 = connCmpPtr[dec_x2_codes[i]].x2;
			subwordPtr[s].y1 = connCmpPtr[dec_x2_codes[i]].y1;
			subwordPtr[s].y2 = connCmpPtr[dec_x2_codes[i]].y2;
			for( int j = 1; j <= no_of_cmps; j++ ) {
				if( connCmpPtr[j].belongs_to == dec_x2_codes[i] ) {
					if( connCmpPtr[j].x1 < subwordPtr[s].x1 ) subwordPtr[s].x1 = connCmpPtr[j].x1;
					if( connCmpPtr[j].x2 > subwordPtr[s].x2 ) subwordPtr[s].x2 = connCmpPtr[j].x2;
					if( connCmpPtr[j].y1 < subwordPtr[s].y1 ) subwordPtr[s].y1 = connCmpPtr[j].y1;
					if( connCmpPtr[j].y2 > subwordPtr[s].y2 ) subwordPtr[s].y2 = connCmpPtr[j].y2;
				}
			}
			s++;
		}
	}
		
	//Extracting subwords
	IMAGE tmpImg;
	tmpImg.create(x_size, y_size, 8);
	copy_sub_image(source, 0, 0, x_size, y_size, &tmpImg, 0, 0, TRUE);
	int tmpDist;
	s = 1;

	if( !(subwordPtr[s].x2 > x_size/2 + 200) ) {			//This is to check if the image is accessible or not
		
		//clear source image
		for(int xt = 0; xt < x_size; xt++)
			line.pixels[xt] = WHITE;
		for(int yt = 0; yt < y_size; yt++)					
			source->put_line(0, yt, x_size, &line, 0);

		int dist = x_size - subwordPtr[s].x2 - MARGIN;			//Distance to shift the subword
		for( int i = 1; i <= no_of_cmps; i++ ) {
			if( connCmpPtr[dec_x2_codes[i]].belongs_to == 0 ) {
				for( int xt = connCmpPtr[dec_x2_codes[i]].x2; xt >= connCmpPtr[dec_x2_codes[i]].x1; xt-- ) {
					for( int yt = connCmpPtr[dec_x2_codes[i]].y1; yt <= connCmpPtr[dec_x2_codes[i]].y2; yt++ ) {
						if( tmpImg.pixel(xt, yt) == dec_x2_codes[i] ) {
							tmpImg.get_line(xt, yt, 1, &line, 0);
							line.pixels[0] = dec_x2_codes[i];
							source->put_line(xt + dist, yt, 1, &line, 0);
							line.pixels[0] = WHITE;
							source->put_line(xt, yt, 1, &line, 0);
						}
					}
				}
				connCmpPtr[dec_x2_codes[i]].x1 += dist;			//Updating the values stored in the connCmpPtr 
				connCmpPtr[dec_x2_codes[i]].x2 += dist;			//data structure for main components
				for( int j = 1; j <= no_of_cmps; j++) {
					if( connCmpPtr[j].belongs_to == dec_x2_codes[i]) {
						for( int xt = connCmpPtr[j].x1; xt <= connCmpPtr[j].x2; xt++ ) {
							for( int yt = connCmpPtr[j].y1; yt <= connCmpPtr[j].y2; yt++ ) {
								if( tmpImg.pixel(xt, yt) == j ) {
									tmpImg.get_line(xt, yt, 1, &line, 0);
									line.pixels[0] = j;
									source->put_line(xt + dist, yt, 1, &line, 0);
									line.pixels[0] = WHITE;
									source->put_line(xt, yt, 1, &line, 0);
								}
							}
						}
						connCmpPtr[j].x1 += dist;				//Updating the values stored in the connCmpPtr
						connCmpPtr[j].x2 += dist;				//data structure for seconderies
					}
				}
				tmpDist = dist;
				if( s <= no_of_subwords-1 ) {
					dist = dist - (subwordPtr[s].x2 - subwordPtr[s].x1 + 1) + (subwordPtr[s].x2 - subwordPtr[s+1].x2);
					if( (subwordPtr[s].x1 - subwordPtr[s+1].x2) >= SPACE )	//If the original distance between the 
						dist -= (subwordPtr[s].x1 - subwordPtr[s+1].x2);	//subwords is larger than space then
					else													//make it the new distance else make
						dist -= SPACE;										//it SPACE
				}
				subwordPtr[s].x1 += tmpDist;					//Updating the values stored in the subwordPtr
				subwordPtr[s].x2 += tmpDist;					//data structure
				s++;
			}

		}
	}

	no_of_subs = no_of_subwords;			//Returning the number of subwords in the IMAGE to be used by
											//Simple_Segmentation

#undef SPACE 
#undef MARGIN

	return subwordPtr;
}//End of Extract_Seconderies

//Ahmad Nov 21, 2008
DLLSYM IMAGE::Features trackLine(IMAGE* source, int x, int y, int next, int& count)//count: if 0 then return count to featP, else return point of count
{
	IMAGE::Features prevP, nextP;
	prevP.x = x;
	prevP.y = y;
	if (count == 0) count = 1000;
	int NZ = 0;
	int cnt = 0;
	int wctr = x;
	int hctr = y;
	int p[9];
	int tp[9];
	IMAGE::Features points[9];
	IMAGE::Features tpoints[9];
	switch(next)
	{
	case 1:	wctr++;break;
	case 2:	wctr++;hctr--;break;
	case 3:	hctr--;break;
	case 4:	wctr--;hctr--;break;
	case 5:	wctr--;break;
	case 6:	wctr--;hctr++;break;
	case 7:	hctr++;break;
	case 8:	wctr++;hctr++;break;
	}
	
	cnt++;
	bool exit = false;

	while (NZ == 0 && cnt < count)
	{
		p[1] = source->pixel(wctr+1,hctr);
		points[1].x = wctr+1;
		points[1].y = hctr;
		p[2] = source->pixel(wctr+1,hctr-1);
		points[2].x = wctr+1;
		points[2].y = hctr-1;
		p[3] = source->pixel(wctr,hctr-1);
		points[3].x = wctr;
		points[3].y = hctr-1;
		p[4] = source->pixel(wctr-1,hctr-1);
		points[4].x = wctr-1;
		points[4].y = hctr-1;
		p[5] = source->pixel(wctr-1,hctr);
		points[5].x = wctr-1;
		points[5].y = hctr;
		p[6] = source->pixel(wctr-1,hctr+1);
		points[6].x = wctr-1;
		points[6].y = hctr+1;
		p[7] = source->pixel(wctr,hctr+1);
		points[7].x = wctr;
		points[7].y = hctr+1;
		p[8] = source->pixel(wctr+1,hctr+1);
		points[8].x = wctr+1;
		points[8].y = hctr+1;
		
		for (int k = 1; k < 9; k++)
			if (p[k] == source->pixel(wctr,hctr))
				NZ++;

		if (NZ == 1)
			for (int i = 0; i < 100; i++)
				if (source->allFeatures[i].x == wctr && source->allFeatures[i].y == hctr)
				{
					count = cnt;
					return source->allFeatures[i];
				}
		
		if (NZ == 2)
			for (int k = 1; k < 9; k++)
				if (p[k] == source->pixel(wctr,hctr) && (points[k].x != prevP.x || points[k].y != prevP.y))
				{
					NZ = 0;
					nextP.x = points[k].x;
					nextP.y = points[k].y;
					break;
				}

		if (NZ > 2)
		{
			for (int i = 0; i < 100; i++)
				if (source->allFeatures[i].x == wctr && source->allFeatures[i].y == hctr)
				{
					count = cnt;
					return source->allFeatures[i];
				}
			if (prevP.x == x && prevP.y == y)
				for (int k = 1; k < 9; k++)
				{
					if (p[k] == source->pixel(wctr,hctr) && (points[k].x != prevP.x || points[k].y != prevP.y))
					{
						tpoints[1].x = points[k].x+1;
						tpoints[1].y = points[k].y;
						tpoints[2].x = points[k].x+1;
						tpoints[2].y = points[k].y-1;
						tpoints[3].x = points[k].x;
						tpoints[3].y = points[k].y-1;
						tpoints[4].x = points[k].x-1;
						tpoints[4].y = points[k].y-1;
						tpoints[5].x = points[k].x-1;
						tpoints[5].y = points[k].y;
						tpoints[6].x = points[k].x-1;
						tpoints[6].y = points[k].y+1;
						tpoints[7].x = points[k].x;
						tpoints[7].y = points[k].y+1;
						tpoints[8].x = points[k].x+1;
						tpoints[8].y = points[k].y+1;
						for (int i=1; i<9; i++)
							if (prevP.x == tpoints[i].x && prevP.y == tpoints[i].y)
							{
								exit = true;
								break;
							}
						if (exit)
						{
							exit = false;
							continue;
						}
						NZ = 0;
						nextP.x = points[k].x;
						nextP.y = points[k].y;
						break;
					}
				}
			else
			{
				for (int k = 1; k < 9; k++)
					if (p[k] == source->pixel(wctr,hctr) && (points[k].x != prevP.x || points[k].y != prevP.y))
						for (int i = 0; i < 100; i++)
							if (source->allFeatures[i].x == points[k].x && source->allFeatures[i].y == points[k].y)
							{
								count = cnt;
								return source->allFeatures[i];
							}
			}
		}

		prevP.x = wctr;
		prevP.y = hctr;
		wctr = nextP.x;
		hctr = nextP.y;
		cnt++;
	}
	
	/*for (int i = 0; i < 100; i++)
		if (source->allFeatures[i].x == wctr && source->allFeatures[i].y == hctr)
			return source->allFeatures[i];*/
	count = cnt;
	IMAGE::Features err;
	err.x = wctr;
	err.y = hctr;
	err.type = 0;
	return err;
}//End of trackLine


DLLSYM void cut(IMAGE* source, IMAGE* sourcethinned,IMAGE::Features point)
{
	int wctr = point.x;
	int hctr = point.y;
	int p[9];
	int NZ=0, count = 50, tcount = 0, next = 0, t=0, tt=1, x = 0, y;
	int cnt[2] = {0};
	bool exit,exit2 = false;
	IMAGE::Features cutP, prevP;
	IMAGE::Features feat[10];
	IMAGE::Features tfeat[10];
	IMAGE::Features points[9];
	IMAGELINE line;
	line.init();
	line.pixels[0] = source->get_white_level();
	
	prevP = point;
	p[1] = sourcethinned->pixel(wctr+1,hctr);
	p[2] = sourcethinned->pixel(wctr+1,hctr-1);
	p[3] = sourcethinned->pixel(wctr,hctr-1);
	p[4] = sourcethinned->pixel(wctr-1,hctr-1);
	p[5] = sourcethinned->pixel(wctr-1,hctr);
	p[6] = sourcethinned->pixel(wctr-1,hctr+1);
	p[7] = sourcethinned->pixel(wctr,hctr+1);
	p[8] = sourcethinned->pixel(wctr+1,hctr+1);
	feat[0].x = 0;
	for (int i = 1; i < 9; i++)
		if (p[i] != sourcethinned->get_white_level())
		{
			NZ++;
			feat[i] = trackLine(sourcethinned, wctr, hctr, i, t);
			if (feat[i].x > feat[0].x)
			{
				feat[0] = feat[i];
				feat[0].type = i;
			}
			t=0;
		}
	
	/*if ((hctr - feat[0].y) > 20)
		return;*/
	next = feat[0].type;
	for (int i = 1; i < 9; i++)
		tfeat[i] = trackLine(sourcethinned, prevP.x, prevP.y, i, tt);

	for (int i = 0; i < 10; i++)
	{
		NZ=0;
		feat[i] = trackLine(sourcethinned, prevP.x, prevP.y, next, tt);
		
		p[1] = sourcethinned->pixel(feat[i].x+1,feat[i].y);
		points[1].x = feat[i].x+1;
		points[1].y = feat[i].y;
		p[2] = sourcethinned->pixel(feat[i].x+1,feat[i].y-1);
		points[2].x = feat[i].x+1;
		points[2].y = feat[i].y-1;
		p[3] = sourcethinned->pixel(feat[i].x,feat[i].y-1);
		points[3].x = feat[i].x;
		points[3].y = feat[i].y-1;
		p[4] = sourcethinned->pixel(feat[i].x-1,feat[i].y-1);
		points[4].x = feat[i].x-1;
		points[4].y = feat[i].y-1;
		p[5] = sourcethinned->pixel(feat[i].x-1,feat[i].y);
		points[5].x = feat[i].x-1;
		points[5].y = feat[i].y;
		p[6] = sourcethinned->pixel(feat[i].x-1,feat[i].y+1);
		points[6].x = feat[i].x-1;
		points[6].y = feat[i].y+1;
		p[7] = sourcethinned->pixel(feat[i].x,feat[i].y+1);
		points[7].x = feat[i].x;
		points[7].y = feat[i].y+1;
		p[8] = sourcethinned->pixel(feat[i].x+1,feat[i].y+1);
		points[8].x = feat[i].x+1;
		points[8].y = feat[i].y+1;

		for (int k = 1; k < 9; k++)
			if (p[k] != sourcethinned->get_white_level())
				NZ++;
			
		t = i+1;
		if (NZ > 2)
		{
			for (int k = 1; k < 9; k++)
				if (p[k] != sourcethinned->get_white_level())
					if (points[k].x != prevP.x || points[k].y != prevP.y)
					{
						for (int l = 1; l < 9; l++)
							if (points[k].x == tfeat[l].x && points[k].y == tfeat[l].y)
							{
								exit2 = true;
								break;
							}
						if (exit2)
							exit2 = false;							
						else
							next = k;
					}
					else
						exit = false;

			
			if (exit)
				break;
			exit = true;
		}
		else
			for (int k = 1; k < 9; k++)
				if (p[k] != sourcethinned->get_white_level() && (points[k].x != prevP.x || points[k].y != prevP.y))
					next = k;

		prevP = feat[i];
		
	}
	
	for (int i = 0; i < t; i++)
	{
		x = feat[i].x;
		y = feat[i].y;
		while(source->pixel(x,y) != source->get_white_level())
		{
			y++;
			tcount++;
		}
		y = feat[i].y-1;
		while(source->pixel(x,y) != source->get_white_level() && tcount > 1)
		{
			y--;
			tcount++;
		}
		if (tcount < count)
		{
			count = tcount;
			cutP = feat[i];
		}
		tcount=0;
	}
	if (count > 25)
		return;
		
	x = cutP.x;
	y = cutP.y;
	while(source->pixel(x,y) != source->get_white_level())
	{
		source->put_line(x,y,1,&line,0);
		y++;
	}
	y = cutP.y-1;
	while(source->pixel(x,y) != source->get_white_level())
	{
		source->put_line(x,y,1,&line,0);
		y--;
	}


}//End of cut
//Ahamd end


DLLSYM void Find_All_Baselines(IMAGE *source, int &Y0, int &Yhalf, int &Y1, int &Y2)
{
	int h=source->get_ysize()-40;//height of original image
	IMAGE half_image;
	half_image.create(source->get_xsize(), h/2, 1);
	copy_sub_image(source,0,20 + h/4,source->get_xsize(),h/2,&half_image,0,0,1);
	Y0 = Find_BaseLine(source);
	Yhalf=Find_BaseLine(&half_image) + h/4 + 20;
	if (source->get_xsize()/2 >600)
		Find_MultiBaseLines(source, Y1, Y2);
	else
	{
		Y1=Y0;
		Y2=Y0;
	}
}//End of Find_All_Baseline


//finds multiple baselines, y1,y2,...(left to right)
DLLSYM void Find_MultiBaseLines(IMAGE *source, int &y1, int &y2) 
{
	//int max = 10;
	IMAGELINE line;
	line.init();
	int x1=20,x2=0,x21=0,xt=0;
	int* a = new int[source->get_xsize()];
	for(int j=0; j< source->get_xsize(); j++)
		a[j]=0;
	
	for(int i = 20; i < source->get_xsize(); i++) 
	{
		/*if (i >= max) 
		{
			max = max * 2;
			int* temp = new int[max];
			for (int j=0; j<i; j++) 
				temp[j] = a[j];
			delete [] a;
			a = temp;
		}*/
		
		source->get_column(i,0,source->get_ysize(),&line,0);
		for(int s = 0; s < source->get_ysize(); s++) 
			if(line.pixels[s]!=source->get_white_level())
			{
				a[i] = 1;
				x2 = i;
				if (a[i-1]==1)
					x1 = i;
				if (x2-x1 > x21)
				{
					x21 = x2-x1;
					xt = x2;
				}
				break;
			}
	}
	IMAGE img1,img2;
	img1.create(xt, source->get_ysize(), 1);
	copy_sub_image(source, 0, 0, xt, source->get_ysize(), &img1,0,0,1);
	img2.create(source->get_xsize() - xt, source->get_ysize(), 1);
	copy_sub_image(source, xt, 0, source->get_xsize() - xt, source->get_ysize(), &img2,0,0,1);
	y1 = Find_BaseLine(&img1);
	y2 = Find_BaseLine(&img2);
	//y[2] = xt;
}//End of Find_MultiBaseLines


//////////////////////////////////////
//Find BaseLine using horizontal projection
//(0,0) lower left corner of current image
DLLSYM int Find_BaseLine(IMAGE *source) 
{
	IMAGELINE line;
	line.init();
	int pix=0;
	int y=0;
	int *dynamic_arr;
	dynamic_arr=(int *) malloc(sizeof(int) * source->get_ysize());
	for(int j=0; j< source->get_ysize(); j++)
		dynamic_arr[j]=0;
	for(int i = source->get_ysize() - 1; i >= 0;  i--) 
	{
		source->get_line(0,i,source->get_xsize(),&line,0);
		for(int s=0;s< source->get_xsize();s++) 
			if(line.pixels[s] != source->get_white_level())
				dynamic_arr[i]++;
		if (dynamic_arr[i] > pix)// && i > (20+h/3) && i < (20+((2*h)/3)))
		{
			pix = dynamic_arr[i];
			y = i;
		}
	}
	delete []dynamic_arr;
	return y;

	//////draw image with baselines
	//IMAGE bl;
	//bl.create(source->get_xsize(), source->get_ysize(), 1);
	//copy_sub_image(source,0,0,source->get_xsize(),source->get_ysize(),&bl,0,0,0);
	//bl.get_line(0,y,source->get_xsize(),&line,0);
	//for(int k = source->get_xsize() - 1; k >= 0;  k-=4) 
	//	line.pixels[k] = 1;
	//bl.put_line(0,y,source->get_xsize(),&line,0);
	//
	//int y1t = source->get_ysize() -y1 -20 -1;
	//int y2t = source->get_ysize() -y2 -20 -1;
	//for(int k = 20; k<x+20; k++)
	//{
	//	line.pixels[0] = 1;
	//	bl.put_line(k,y1t + k*(y2t-y1t)/x,1,&line,0);
	//}
	//bl.write((char*)(void*)System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi("z"+file->Name));
	//
}//End of Find_Baseline

DLLSYM int Find_Feature_Points(IMAGE *source, connCmpInf* connCmpPtr, int right, int left) 
{
	int no_of_FPs = 0; 
	
	int wctrUB = source->get_xsize();
	int hctrUB = source->get_ysize();
	int hctr, wctr;
	int white = source->get_white_level();

	int iFeaturePoints[256];
	int p[9];
	int NT, NZ, PrevPoint;

	int EndPoints = 0;
	int BranchPoints = 0;
	int CrossPoints = 0;
	int AscDesPoints = 0;
//	int PrevX = -10;
//	PrevY = -10;
	//Thinner(source);
	for (int i=0; i<256; i++)
		iFeaturePoints[i] = 0;

	// Find the color of the 8 neighbors
	for(wctr = right; wctr >= left; wctr--) 
		for(hctr = hctrUB - 1; hctr >= 0; hctr--)
			if (!(connCmpPtr[(source->pixel(wctr,hctr)==source->get_white_level())?0:source->pixel(wctr,hctr)].is_secondery))
				if(source->pixel(wctr,hctr) != white) 
				{
					p[1] = source->pixel(wctr+1,hctr);
					p[2] = source->pixel(wctr+1,hctr-1);
					p[3] = source->pixel(wctr,hctr-1);
					p[4] = source->pixel(wctr-1,hctr-1);
					p[5] = source->pixel(wctr-1,hctr);
					p[6] = source->pixel(wctr-1,hctr+1);
					p[7] = source->pixel(wctr,hctr+1);
					p[8] = source->pixel(wctr+1,hctr+1);

					//iFeaturePoints[1*p[1] +2*p[2] +4*p[3] +8*p[4] +16*p[5] +32*p[6] +64*p[7] +128*p[8]] ++;

					//find number of black neighbors
					NZ = 0;
					for (int i=1; i<9; i++)
						if (p[i] != source->get_white_level())
							NZ++;

					//find number of transitions from 0 to 1
					NT = 0;
					PrevPoint = p[8];
					for (int i=1; i<9; i++) {
						if (PrevPoint==source->get_white_level() && p[i] != source->get_white_level())
							NT++;
						PrevPoint = p[i];
					}

					if (NZ == 1)
					{
						source->allFeatures[no_of_FPs].type = END_POINT;
						source->allFeatures[no_of_FPs].x = wctr;
						source->allFeatures[no_of_FPs].y = hctr;
						no_of_FPs++;
						EndPoints++;
					}
					if (NT == 3) {
						source->allFeatures[no_of_FPs].type = BRANCH_POINT;
						source->allFeatures[no_of_FPs].x = wctr;
						source->allFeatures[no_of_FPs].y = hctr;
						no_of_FPs++;
						BranchPoints++;
					}
					/*					if((BranchPoints>0) && (abs(wctr-PrevX)<2) && (abs(hctr-PrevY)<2)) {
					// convert to branch points to one cross point
					BranchPoints--;
					CrossPoints++;
					PrevX = -10;
					PrevY = -10;
					}
					else {
					BranchPoints++;
					PrevX = wctr;
					PrevY = hctr;
					}
					}*/
					if (NT == 4)
					{
						source->allFeatures[no_of_FPs].type = CROSS_POINT;
						source->allFeatures[no_of_FPs].x = wctr;
						source->allFeatures[no_of_FPs].y = hctr;
						no_of_FPs++;
						CrossPoints++;
					}

				} //end of if

				/////////////////////////////
				//find ascenders & descenders
				int direction[9];
				char dir, dir0; 
				int nextP, prevP;
				for(int i = 0; i < no_of_FPs; i++)
					if(source->allFeatures[i].type == END_POINT)
					{
						for(int z = 1; z < 9; z++) direction[z]=0;
						nextP = 0;
						prevP = 0;
						NZ = 0;
						wctr = source->allFeatures[i].x;
						hctr = source->allFeatures[i].y;

						for (int j = 0; j < 25; j++)
						{
							p[1] = source->pixel(wctr+1,hctr);
							p[2] = source->pixel(wctr+1,hctr-1);
							p[3] = source->pixel(wctr,hctr-1);
							p[4] = source->pixel(wctr-1,hctr-1);
							p[5] = source->pixel(wctr-1,hctr);
							p[6] = source->pixel(wctr-1,hctr+1);
							p[7] = source->pixel(wctr,hctr+1);
							p[8] = source->pixel(wctr+1,hctr+1);

							for (int k = 0; k < 9; k++)
							{
								if (p[k] == source->pixel(wctr,hctr))
								{
									NZ++;
									if (((prevP > 4) && (prevP - 4 == k)) || ((prevP <= 4) && (prevP != 0) && (prevP + 4 == k)))
										continue;//k loop
									else
									{
										direction[k]++;
										nextP = k;
									}
								}
							}

							prevP = nextP;
							if(NZ == 1 || NZ == 2)
							{
								switch(nextP)
							{
								case 1:	wctr++;break;
								case 2:	wctr++;hctr--;break;
								case 3:	hctr--;break;
								case 4:	wctr--;hctr--;break;
								case 5:	wctr--;break;
								case 6:	wctr--;hctr++;break;
								case 7:	hctr++;break;
								case 8:	wctr++;hctr++;break;
							}
								NZ = 0;
							}
							else
								break;//j loop
						}

						if (NZ > 0)
							continue;//i loop

						if (max(direction[1],direction[5]) == max(direction[3],direction[7]))
						{
							while (max(direction[1],direction[5]) == max(direction[3],direction[7]) && NZ == 0)
							{
								p[1] = source->pixel(wctr+1,hctr);
								p[2] = source->pixel(wctr+1,hctr-1);
								p[3] = source->pixel(wctr,hctr-1);
								p[4] = source->pixel(wctr-1,hctr-1);
								p[5] = source->pixel(wctr-1,hctr);
								p[6] = source->pixel(wctr-1,hctr+1);
								p[7] = source->pixel(wctr,hctr+1);
								p[8] = source->pixel(wctr+1,hctr+1);

								for (int k = 0; k < 9; k++)
								{
									if (p[k] == source->pixel(wctr,hctr))
									{
										NZ++;
										if (((prevP > 4) && (prevP - 4 == k)) || ((prevP <= 4) && (prevP != 0) && (prevP + 4 == k)))
											continue;//k loop
										else
										{
											direction[k]++;
											nextP = k;
										}
									}
								}

								prevP = nextP;
								if(NZ == 1 || NZ == 2)
								{
									switch(nextP)
									{
									case 1:	wctr++;break;
									case 2:	wctr++;hctr--;break;
									case 3:	hctr--;break;
									case 4:	wctr--;hctr--;break;
									case 5:	wctr--;break;
									case 6:	wctr--;hctr++;break;
									case 7:	hctr++;break;
									case 8:	wctr++;hctr++;break;
									}
									NZ = 0;
								}
								else
									break;//while
							}

							if (NZ > 0)
								continue;//i loop
						}
						
						if (max(direction[1],direction[5]) > max(direction[3],direction[7]))
							dir = dir0 = 'h';
						else
							dir = dir0 = 'v';

						while (dir == dir0)
						{
							p[1] = source->pixel(wctr+1,hctr);
							p[2] = source->pixel(wctr+1,hctr-1);
							p[3] = source->pixel(wctr,hctr-1);
							p[4] = source->pixel(wctr-1,hctr-1);
							p[5] = source->pixel(wctr-1,hctr);
							p[6] = source->pixel(wctr-1,hctr+1);
							p[7] = source->pixel(wctr,hctr+1);
							p[8] = source->pixel(wctr+1,hctr+1);

							for (int k = 0; k < 9; k++)
							{
								if (p[k] == source->pixel(wctr,hctr))
								{
									NZ++;
									if (((prevP > 4) && (prevP - 4 == k)) || ((prevP <= 4) && (prevP != 0) && (prevP + 4 == k)))
										continue;
									else
									{
										if ((dir0 == 'h' && k == 3) || (dir0 == 'h' && k == 7) ||
											(dir0 == 'v' && k == 1) || (dir0 == 'v' && k == 5))
										{
											dir = (dir0 == 'h')?'v':'h';
											if (k != 3)//k != 5
												NZ = 0;
											else
												NZ = 10;
											break;
										}
										else
										{
											direction[k]++;
											nextP = k;
										}
									}
								}
							}

							if(NZ == 1 && prevP != 0)
								break;
							else
								prevP = nextP;
							if(NZ == 1 || NZ == 2)
							{
								switch(nextP)
								{
								case 1:	wctr++;break;
								case 2:	wctr++;hctr--;break;
								case 3:	hctr--;break;
								case 4:	wctr--;hctr--;break;
								case 5:	wctr--;break;
								case 6:	wctr--;hctr++;break;
								case 7:	hctr++;break;
								case 8:	wctr++;hctr++;break;
								}
								NZ = 0;
							}
							else
								break;
						}

						for (int h = hctr + 3; h > hctr - 4; h--)
							for (int w = wctr + 3; w > wctr - 4; w--)
								for (int l = 0; l < no_of_FPs; l++)
									if (w == source->allFeatures[l].x && h == source->allFeatures[l].y)
									{
										NZ = source->allFeatures[l].type;
										w=h=0;
										break;
									}

						if (EndPoints == 2 && AscDesPoints == 1 && BranchPoints == 0 && CrossPoints == 0)
							continue;

						if (NZ == 0)
						{
							source->allFeatures[no_of_FPs].type = ASC_DES;
							source->allFeatures[no_of_FPs].x = wctr;
							source->allFeatures[no_of_FPs].y = hctr;
							AscDesPoints++;
							no_of_FPs++;
						}
					}
					return no_of_FPs;
}//End of Find_Feature_Points

//reads a specific info from the tru file, like ZIP or AW1 ...
void gettru(FILE* f, char* info, char* z)
{
	int x;
	char* y = new char[5];
	
	long size, pos;
	pos=ftell (f);
	fseek (f, 0, SEEK_END);
    size=ftell (f);
	fseek (f, pos, SEEK_SET);
	
	fgets(y,5,f);
	int i=0;
	while (strcmp(y,z) != 0)
	{
		if (ftell (f) == size)
			break;
		i++;
		fseek(f, i, SEEK_SET);
		fgets(y,5,f);
	}
	if (ftell (f) != size)
		if (strcmp("ZIP:",z) == 0 || strcmp("AW1:",z) == 0 || strcmp("AW2:",z) == 0 || strcmp("QUA:",z) == 0 || strcmp("ADD:",z) == 0)
		{
			if (strcmp("ADD:",z) == 0)
			{
				int i=0;
				while ((x=fgetc(f)) != '\n')
				{
					info[i]=x;
					i++;
				}
				info[i]=NULL;
			}
			else
			{
				int i=0;
				while ((x=fgetc(f)) != ';')
				{
					info[i]=x;
					i++;
				}
				info[i]=NULL;
			}
		}
		else
		{
			fgetc(f);
			int i=0;
			while ((x=fgetc(f)) != '\n')
			{
				info[i]=x;
				i++;
			}
			info[i]=NULL;
		}
	else
		info[0]=NULL;
}//End of gettru



//reads y1,y2 from the tru file which must be in a 'tru' directory with the same path of the 'tif' directory 
DLLSYM void get_BLN(int &y1tru, int &y2tru)
{
	char* trupath=new char[100];
	strcpy(trupath,__argv[1]);
	
	char* truf;
	truf = strstr(trupath,"tif");
	strncpy (truf,"tru",3);
	truf = strstr(trupath,"tif");
	strncpy (truf,"tru",3);
	
	FILE* f = fopen(trupath,"r");
	tru* truinfo=new tru(20);
	gettru(f, truinfo->BLN, "BLN:");
	fclose(f);

	//find Y1 and Y2
	int r=0;
	int y1=0;
	int y2=0;
	//find y1
	for (r=0; r<5; r++)
		if(truinfo->BLN[r] != ',')
			y1=y1*10+truinfo->BLN[r]-48;
		else
			break;
	//find y2
	r++;
	for (r; r<10; r++)
		if(truinfo->BLN[r] != NULL)
			y2=y2*10+truinfo->BLN[r]-48;
		else
			break;
	y1tru = y1;
	y2tru = y2;
}//End of get_BLN

//writes all baselines to a csv file in the current directory
DLLSYM void writeBLtoCSV(IMAGE *source, int y1tru, int y2tru, int y, int yhalf, int y1, int y2)
{
	//write to baselines.csv
	if (y1==0) y1=y2;
	char* name = strrchr(__argv[1], '\\');
	name++;
	char* current_dir = new char[100];
	GetCurrentDirectory(100,current_dir);
	strcat(current_dir,"\\baselines.csv");
	FILE* bl;
	bl = fopen(current_dir,"a");
	
	fprintf(bl,"%s,%d,%d,%d,%d,%d,%d,%d\n", 
		name, 
		source->get_ysize()-40, 
		y1tru, 
		y2tru, 
		source->get_ysize() - y - 20,
		source->get_ysize() - 20 - yhalf,
		source->get_ysize() - 20 - y1,
		source->get_ysize() - 20 - y2);
	fclose(bl);
}//End of writeBLtoCSV

DLLSYM void Write_Baselines(IMAGE *source, int Y0, int Yhalf, int Y1, int Y2)
{
	int y1tru, y2tru;
	get_BLN(y1tru, y2tru);
	char* current_dir = new char[100];
	GetCurrentDirectory(100,current_dir);
	strcat(current_dir,"\\baselines.csv");
	FILE* bl;
	bl = fopen(current_dir,"r");
	if (bl == NULL)
	{
		bl = fopen(current_dir,"a");
		fputs("File Name,H,Y1tru,Y2tru,Y0,Yhalf,Y1,Y2\n",bl);
		fclose(bl);
	}
	else
		fclose(bl);
	writeBLtoCSV(source, y1tru, y2tru, Y0, Yhalf, Y1, Y2);
}//End of Write_Baselines

//reads a specific info from the tif file, like ZIP or AW1 ...
void gettif(FILE* f, char* info, char* z)
{
	int x;
	char* y = new char[5];
	fgets(y,5,f);
	int i=0;
	while (strcmp(y,z) != 0)
	{
		i++;
		fseek(f, i, SEEK_SET);
		fgets(y,5,f);
	}
	
	i=0;
	if (strcmp("ADD:",z) == 0)
	{
		while ((x=fgetc(f)) != 0)
		{
			info[i]=x;
			i++;
		}
		info[i]=NULL;
	}
	else
	{
		while ((x=fgetc(f)) != ';')
		{
			info[i]=x;
			i++;
		}
		info[i]=NULL;
	}
}//End of gettif


//reads all info in a tif or tru file and puts it in a struct
void get_info(FILE* f, tif* info, tru* truinfo)
{
	f = fopen(__argv[1],"r");
	
	char* ext = strrchr(__argv[1], '.');
	ext++;
	if (strcmp(ext, "tif"))
	{
		char* name = strrchr(__argv[1], '\\');
		name++;
		info->FILENAME = name;
		gettif(f, info->ZIP, "ZIP:");
		gettif(f, info->AW1, "AW1:");
		gettif(f, info->AW2, "AW2:");
		gettif(f, info->QUA, "QUA:");
		gettif(f, info->ADD, "ADD:");
	}
	if (strcmp(ext, "tru"))
	{
		char* name = strrchr(__argv[1], '\\');
		name++;
		truinfo->FILENAME = name;
		gettru(f, truinfo->SOURCE, "from");
		gettru(f, truinfo->X_Y, "X_Y:");
		gettru(f, truinfo->ZIP, "ZIP:");
		gettru(f, truinfo->AW1, "AW1:");
		gettru(f, truinfo->AW2, "AW2:");
		gettru(f, truinfo->QUA, "QUA:");
		gettru(f, truinfo->ADD, "ADD:");
		gettru(f, truinfo->CHA, "CHA:");
		gettru(f, truinfo->BLN, "BLN:");
		gettru(f, truinfo->TLN, "TLN:");
	}

	fclose(f);
}//End of get_info


DLLSYM void Thinner(IMAGE *source)
{
	//mysource->write("step4.tif");
	IMAGELINE line;
	line.init();
	
	IMAGE  temp_img, mysource;
	
	//Ahmad Sep 17, 2008
	mysource.create(source->get_xsize(), source->get_ysize(), 1);
	copy_sub_image(source,0,0,source->get_xsize(),source->get_ysize(),&mysource ,0,0,1);
	//Ahmad end
	temp_img.create(mysource.get_xsize(), mysource.get_ysize(), mysource.get_bpp());
	copy_sub_image(&mysource,0,0,mysource.get_xsize(),mysource.get_ysize(),&temp_img ,0,0,0);
	
	/*source->write("source.tif");
	mysource.write("mysource.tif");
	temp_img.write("temp.tif");
	*/
	int wctr = 0;
	int hctr = 0;
	int wctrUB = mysource.get_xsize();
	int hctrUB = mysource.get_ysize();
	bool DFlag = FALSE;
	int NT, NZ, PrevPoint;
	int x[9];
	
	//this loop clears temp_img
	for (int j = hctrUB-1; j >=0 ; --j)
	{
		temp_img.get_line(0, j, wctrUB, &line, 0);
		
		for(int s=0;s<=wctrUB;s++)
			line.pixels[s]=0;
			
		temp_img.put_line(0, j, wctrUB, &line, 0);
	}

	//this loop reverses the colors of the image 
	for (int j = hctrUB-1; j >=0 ; --j)
	{
		mysource.get_line(0, j, wctrUB, &line, 0);
		
		for(int s=0;s<=wctrUB;s++)
		{
			if(line.pixels[s]==0)
				line.pixels[s]=1;
			else
				line.pixels[s]=0;
		}

		mysource.put_line(0, j, wctrUB, &line, 0);
	}
	
	while(DFlag == FALSE)			
	{
		DFlag = TRUE;
		wctrUB = mysource.get_xsize();
		hctrUB = mysource.get_ysize();

		// Iteration 1 ------------------------------
		for(hctr = hctrUB - 2; hctr > 0; hctr--)
		{
			for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
			{
				if(mysource.pixel(wctr,hctr))
				{
					/*x[1] = mysource.pixel(wctr+1,hctr);
					x[2] = mysource.pixel(wctr+1,hctr-1);
					x[3] = mysource.pixel( wctr ,hctr-1);
					x[4] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[6] = mysource.pixel(wctr-1,hctr+1);
					x[7] = mysource.pixel( wctr ,hctr+1);
					x[8] = mysource.pixel(wctr+1,hctr+1);*/
					x[1] = mysource.pixel(wctr+1,hctr);
					x[8] = mysource.pixel(wctr+1,hctr-1);
					x[7] = mysource.pixel( wctr ,hctr-1);
					x[6] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[4] = mysource.pixel(wctr-1,hctr+1);
					x[3] = mysource.pixel( wctr ,hctr+1);
					x[2] = mysource.pixel(wctr+1,hctr+1);

					// find number of black neighbors
					NZ = 0;
					for (int i=1; i<9; i++)
						if (x[i])
							NZ++;
					
					// find number of 01 transitions
					NT = 0;
					PrevPoint = x[8];
					for (int i=1; i<9; i++) 
					{
						if (PrevPoint==0 && x[i]!=0)
							NT++;
						PrevPoint = x[i];
					}

					if ((NT <= 2) &&
						(NZ !=1) &&
						((x[1] & x[3] & x[5])==0) &&
						((x[1] & x[3] & x[7])==0) && (NZ !=0))
					{
						if (NT != 2) 
						{
							temp_img.get_line(wctr, hctr, 1, &line, 0);
							line.pixels[0]=1;
							temp_img.put_line(wctr, hctr, 1, &line, 0);
							DFlag = FALSE;
						}
						else 
							if ((((x[1]&x[7])==1) && ((x[2]|x[6])==1) &&
 ((x[3]|x[4]|x[5]|x[8])==0)) ||
							    (((x[1]&x[3])==1) && ((x[4]|x[8])==1) &&
 ((x[2]|x[5]|x[6]|x[7])==0))) 
							{
								temp_img.get_line(wctr, hctr, 1, &line, 0);
								line.pixels[0]=1;
								temp_img.put_line(wctr, hctr, 1, &line, 0);
								DFlag = FALSE;
							}
					}
				} // end if
			}  // end for
		}  // end for
		
		for(hctr = hctrUB - 2; hctr > 0; hctr--)
			for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
				if(temp_img.pixel(wctr,hctr) == 1)
				{
					mysource.get_line(wctr, hctr, 1, &line, 0);
					line.pixels[0]=0;
					mysource.put_line(wctr, hctr, 1, &line, 0);
				}
		
		// Iteration 2 ------------------------------
		for(hctr = hctrUB - 2; hctr > 0; hctr--)
		{
			for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
			{
				if(mysource.pixel(wctr,hctr))
				{
					/*x[1] = mysource.pixel(wctr+1,hctr);
					x[2] = mysource.pixel(wctr+1,hctr-1);
					x[3] = mysource.pixel( wctr ,hctr-1);
					x[4] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[6] = mysource.pixel(wctr-1,hctr+1);
					x[7] = mysource.pixel( wctr ,hctr+1);
					x[8] = mysource.pixel(wctr+1,hctr+1);*/
					x[1] = mysource.pixel(wctr+1,hctr);
					x[8] = mysource.pixel(wctr+1,hctr-1);
					x[7] = mysource.pixel( wctr ,hctr-1);
					x[6] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[4] = mysource.pixel(wctr-1,hctr+1);
					x[3] = mysource.pixel( wctr ,hctr+1);
					x[2] = mysource.pixel(wctr+1,hctr+1);

					// find number of black neighbors
					NZ = 0;
					for (int i=1; i<9; i++)
						if (x[i])
							NZ++;
					
					// find number of 01 transitions
					NT = 0;
					PrevPoint = x[8];
					for (int i=1; i<9; i++) 
					{
						if (PrevPoint==0 && x[i]!=0)
							NT++;
						PrevPoint = x[i];
					}

					if ((NT <= 2) &&
						(NZ !=1) &&
						((x[3] & x[5] & x[7])==0) &&
						((x[5] & x[7] & x[1])==0) && (NZ !=0)) 
					{
							if (NT != 2) 
							{
								temp_img.get_line(wctr, hctr, 1, &line, 0);
								line.pixels[0]=1;
								temp_img.put_line(wctr, hctr, 1, &line, 0);
								DFlag = FALSE;
							}
							else if ((((x[5]&x[3])==1) && ((x[6]|x[2])==1) &&
 ((x[1]|x[4]|x[7]|x[8])==0)) ||
									 (((x[7]&x[5])==1) && ((x[8]|x[4])==1) &&
 ((x[1]|x[2]|x[3]|x[6])==0))) 
							{ //5672
									temp_img.get_line(wctr, hctr, 1, &line, 0);
									line.pixels[0]=1;
									temp_img.put_line(wctr, hctr, 1, &line, 0);
									DFlag = FALSE;
							}
					}
				} // end if
			}  // end for
		}  // end for
		
		for(hctr = hctrUB - 2; hctr > 0; hctr--)
			for(wctr = wctrUB - 2; wctr > 0 ; wctr--)
				if(temp_img.pixel(wctr,hctr) == 1)
				{
					mysource.get_line(wctr, hctr, 1, &line, 0);
					line.pixels[0]=0;
					mysource.put_line(wctr, hctr, 1, &line, 0);
				}
	}
		
		// begin stair case removal
		int index;
		for(hctr = hctrUB - 2; hctr > 0; hctr--)
		{
			for(wctr = wctrUB - 2; wctr > 0; wctr--)
			{
				if(mysource.pixel(wctr,hctr))
				{
					/*x[1] = mysource.pixel(wctr+1,hctr);
					x[2] = mysource.pixel(wctr+1,hctr-1);
					x[3] = mysource.pixel( wctr ,hctr-1);
					x[4] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[6] = mysource.pixel(wctr-1,hctr+1);
					x[7] = mysource.pixel( wctr ,hctr+1);
					x[8] = mysource.pixel(wctr+1,hctr+1);*/
					x[1] = mysource.pixel(wctr+1,hctr);
					x[8] = mysource.pixel(wctr+1,hctr-1);
					x[7] = mysource.pixel( wctr ,hctr-1);
					x[6] = mysource.pixel(wctr-1,hctr-1);
					x[5] = mysource.pixel(wctr-1,hctr);
					x[4] = mysource.pixel(wctr-1,hctr+1);
					x[3] = mysource.pixel( wctr ,hctr+1);
					x[2] = mysource.pixel(wctr+1,hctr+1);

					index = 1*x[1] +2*x[2] +4*x[3] +8*x[4] +16*x[5] +32*x[6] +64*x[7]
 +128*x[8];

					// remove staircase
					if ((index == 5) ||
						(index == 20) ||
						(index == 80) ||
						(index == 65)) 
					{
						mysource.get_line(wctr, hctr, 1, &line, 0);
						line.pixels[0]=0;
						mysource.put_line(wctr, hctr, 1, &line, 0);
					}

					// remove extra points
					if ((x[1]+x[3]+x[5]+x[7]) == 3)
					{
						mysource.get_line(wctr, hctr, 1, &line, 0);
						line.pixels[0]=0;
						mysource.put_line(wctr, hctr, 1, &line, 0);
					}
				} // end if
			}  // end for
		}  // end for
		// end stair case removal

		//this loop reverses back the colors of the image 
		for (int j = hctrUB-1; j >=0 ; --j)
		{
			mysource.get_line(0, j, wctrUB, &line, 0);

			for(int s=0;s<wctrUB;s++)
			{
				if(line.pixels[s]==0)
					line.pixels[s]=1;
				else
					line.pixels[s]=0;
			}

			mysource.put_line(0, j, wctrUB, &line, 0);
		}				
	
		//mysource.write("test_thn6.tif");
		
		
		//Ahmad Sep 17, 2008
		line.pixels[0]=source->get_white_level();
		for(hctr = hctrUB - 1; hctr >= 0; hctr--)
			for(wctr = wctrUB - 1; wctr >= 0; wctr--)
				if (mysource.pixel(wctr,hctr) == mysource.get_white_level() &&
					source->pixel(wctr,hctr) != source->get_white_level())
						source->put_line(wctr,hctr,1,&line,0);
		//source->write("test_thn6.tif");
		//Ahmad end
}//End of Thinner



DLLSYM void Assign_Seconderies_Letters(IMAGE* source,
									   int no_of_cmps, 
									   connCmpInf* connCmpPtr, 
									   BYTE* &inc_x1_codes, 
									   BYTE* &dec_x2_codes )
//(Nadine)
{
	//Defining connected components properties:   << THE COUNTER IN THE LOOP SHOULD BE 'i' >>
#define X1				(connCmpPtr[inc_x1_codes[i]].x1)
#define X2				(connCmpPtr[inc_x1_codes[i]].x2)
#define Y1				(connCmpPtr[inc_x1_codes[i]].y1)
#define Y2				(connCmpPtr[inc_x1_codes[i]].y2)
#define IS_SEC			(connCmpPtr[inc_x1_codes[i]].is_secondery)
#define BELONGS_TO		(connCmpPtr[inc_x1_codes[i]].belongs_to)
#define GROUP_SEC	    (connCmpPtr[inc_x1_codes[i]].groupSecondery)

	//Variables definitions:
	int x_size = source->get_xsize();
	int y_size = source->get_ysize();
	int i = 0;										//For traversing the connected components
	int j = 0;
	int distance1 = 0;
	int distance2 = 0;
	int r; //used when going through elements on the right
	int l; //used when going through elements on the left
	int xmid;

	BYTE WHITE = source->get_white_level();

	IMAGELINE line;
	line.init();

	//Variables needed for ordering the connected components based on their x values.
	//We need to traverse the components in two ways: from left to right -> increasing x1
	//												: from right to left -> decreasing x2

	int *inc_x1_x1s , *dec_x2_x2s;
	inc_x1_codes = new BYTE[no_of_cmps+1];
	dec_x2_codes = new BYTE[no_of_cmps+1];
	inc_x1_x1s = new int[no_of_cmps+1];
	dec_x2_x2s = new int[no_of_cmps+1];
	BYTE tmpColor;
	int tmpX;

	//Initializing the arrays of colors' codes and the arrays of x1s and x2s
	for( int k = 1; k <= no_of_cmps; k++ ) {	
		inc_x1_codes[k] = k;
		inc_x1_x1s[k] = connCmpPtr[k].x1;
		dec_x2_codes[k] = k;
		dec_x2_x2s[k] = connCmpPtr[k].x2;
	}

	//Storing the codes of the components with increasing x1 in the array inc_x1_codes.
	//After executing this loop: inc_x1_codes[1] is the CODE of the leftmost component.
	//							 inc_x1_codes[no_of_cmps] is the CODE of the rightmost component.
	for( i = 1; i < no_of_cmps; i++ ) {
		for( j = 1; j < no_of_cmps; j++ ) 
			if( inc_x1_x1s[j] > inc_x1_x1s[j+1] ) {		//Swapping
				tmpColor = inc_x1_codes[j];
				inc_x1_codes[j] = inc_x1_codes[j+1];
				inc_x1_codes[j+1] = tmpColor;

				tmpX = inc_x1_x1s[j];
				inc_x1_x1s[j] = inc_x1_x1s[j+1];
				inc_x1_x1s[j+1] = tmpX;
			}
	}

	//Storing the codes of the components with decreasing x2 in the array dec_x2_codes.
	//After executing this loop: dec_x2_codes[1] is the CODE of the rightmost component.
	//							 dec_x2_codes[no_of_cmps] is the CODE of the leftmost component.
	for( i = 1; i < no_of_cmps; i++ ) {
		for( j = 1; j < no_of_cmps; j++ ) 
			if( dec_x2_x2s[j] < dec_x2_x2s[j+1] ) {		//Swapping
				tmpColor = dec_x2_codes[j];
				dec_x2_codes[j] = dec_x2_codes[j+1];
				dec_x2_codes[j+1] = tmpColor;

				tmpX = dec_x2_x2s[j];
				dec_x2_x2s[j] = dec_x2_x2s[j+1];
				dec_x2_x2s[j+1] = tmpX;
			}
	}

	//Assigning seconderies to main bodies
	int minX1, maxX2, minY1, maxY2;
	int image_x2 = connCmpPtr[dec_x2_codes[1]].x2;
	for( i = 1; i <= no_of_cmps; i++ ) {
		if( IS_SEC == true ) {
			//previously in Identify_Seconderies

			minX1 = X1;
			maxX2 = X2;
			minY1 = Y1;
			maxY2 = Y2;
			if( GROUP_SEC != 0 )
			{

				for(int j = 1; j <= no_of_cmps; j++)
					if( j != i && connCmpPtr[inc_x1_codes[j]].groupSecondery == GROUP_SEC)
						//if more than one secondery has the same groupSecondery value, they will be considered as one secondery
						//for the rest of this function.
						//minX1 = x1 of the secondery with the smallest x1 value, and so on.
					{
						minX1 = min(minX1 , connCmpPtr[inc_x1_codes[j]].x1);
						maxX2 = max(maxX2 , connCmpPtr[inc_x1_codes[j]].x2);
						minY1 = min(minY1 , connCmpPtr[inc_x1_codes[j]].y1);
						maxY2 = max(maxY2 , connCmpPtr[inc_x1_codes[j]].y2);

					}
			}

			//Check if secondery belongs to kaf. Notice we use X1 not minX1 here,
			//because the kaf secondery should not be associated with any other seconderies.
			if ((BELONGS_TO == 0) && (Y2 > (y_size - y_size/4)) && ((Y2 - Y1) > y_size/6))
			{
				for (int y1t = Y1; (y1t <= Y1 + 3) && (BELONGS_TO == 0) ; y1t++)
					if((source->pixel(X1, y1t) == inc_x1_codes[i]) && (source->pixel( X1, y1t ) != WHITE))	
					{
						for( int yt = Y1; (yt >= 0) && (BELONGS_TO == 0); yt-- )		
							if( (source->pixel(X1, yt) != WHITE) && (connCmpPtr[source->pixel(X1, yt)].is_secondery == false) )
							{	
								BELONGS_TO = source->pixel(X1, yt);

							}
					}
			}
			xmid = (minX1 + maxX2)/2; //find midpoint of secondary

			int prev_y2;	//used to store previous y value when looking for the letter waw
			int NT=0;		//number of transitions from white to black
			int xtraverse =0;

			//check if main body above midpoint
			for( int yt = minY1; (yt <y_size) && (BELONGS_TO == 0); yt++ )	
				if( (source->pixel(xmid, yt) != WHITE) && (connCmpPtr[source->pixel(xmid, yt)].is_secondery == false))
				{			

					BELONGS_TO = source->pixel(xmid,yt);
					int tmpColor = source->pixel(xmid,yt);
					//checking to see if the letter is a waw
					if(connCmpPtr[source->pixel(xmid,yt)].height < y_size/1.6 && connCmpPtr[source->pixel(xmid,yt)].height >= y_size/3.6)//used to be 3
					//|| connCmpPtr[source->pixel(xmid,yt)].arabChar == RAA2_E && Y2 < connCmpPtr[source->pixel(xmid,yt)].y2)//FUAD:: no seconderies under a raa2, treat as waw
					{

						//secondery will be moved if it is near the right edge of the waw
						if (maxX2 > (connCmpPtr[source->pixel(xmid,yt)].x2 - (connCmpPtr[source->pixel(xmid,yt)].x2 - connCmpPtr[source->pixel(xmid,yt)].x1)/ 8)) 
						{
							if (connCmpPtr[source->pixel(xmid,yt)].groupSecondery != -9)//-9 is assigned to the letter waw once it is found
							{
								int whiteSpaces;//variable to traverse through the white pixels that come to the left of the waw
								bool flag = false;
								//waw comes at the end of a subword therefore there should be at least ten white pixels after it
								for (whiteSpaces = connCmpPtr[source->pixel(xmid,yt)].x1 - 1; (whiteSpaces > (connCmpPtr[source->pixel(xmid,yt)].x1 - 10)) && flag == false; whiteSpaces--)
									for ( int ytt = 0; ytt < y_size; ytt++)
										if (source->pixel(whiteSpaces,ytt) != WHITE)
										{
											flag = true;
											break;
										}

										if (flag == false) //if 10 white columns were found to the left of the waw 			
										{
											//finding x coordinate that goes with y2
											for (int xtr = connCmpPtr[source->pixel(xmid, yt)].x1; xtr <=connCmpPtr[source->pixel(xmid,yt)].x2 - 5; xtr++)
												if (source->pixel(xtr,connCmpPtr[source->pixel(xmid,yt)].y2) != WHITE && connCmpPtr[source->pixel(xtr,connCmpPtr[source->pixel(xmid,yt)].y2)].is_secondery == false)
													xtraverse = xtr;

											for (int ii =0; ii<=3; ii++) //in case y2 is not above the middle circular part of the waw
												//this loop allows us to move to the right a little
											{
												prev_y2 = connCmpPtr[source->pixel(xmid,yt)].y2;
												//moves downwards along one column and counts number of transitions from white to black 
												//should be at least 2 if letter is waw
												for(int ytraverse = connCmpPtr[source->pixel(xmid, yt)].y2; (ytraverse>=0) ; ytraverse--)
												{
													if (source->pixel(xtraverse,ytraverse) != WHITE && source->pixel(xtraverse,prev_y2) == WHITE && connCmpPtr[source->pixel(xtraverse, ytraverse)].is_secondery == false)
														NT++;
													prev_y2= ytraverse;
												}
												if (NT >= 2)// why not NT == 2? due to the ii loop we may start from the right of y2, i.e. from a white pixel
													//which would increase NT by 1
												{	
													BELONGS_TO = 0;//as long as waw was found we need to cancel out the BELONGS_TO value that was assigned before

													//if a letter is found to the right, we want to assign the secondery to it instead of the waw
													for (int around_yt = yt - 6; around_yt <= yt + 6; around_yt++)
														if ((source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt) != WHITE ) && (connCmpPtr[source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt)].is_secondery == false))
															BELONGS_TO = source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 +2, around_yt);
													//Since the groupSecondery variable is 0 for all letters (letter here means not secondery)
													//it is utilized here to indicate the waw. -9 is used because it is a number that couldn't possibly have
													//been assigned to a secondery as it is negative. And 9 looks like the letter waw
													connCmpPtr[source->pixel(xmid,yt)].groupSecondery = -9;
													break;//once the waw is found there is no need to continue the ii loop
												}
												else 
												{
													xtraverse = xtraverse+1;//move to the right
													NT = 0;
												}
											}//for
										}//if flag
							}// if -9
							else //if we already know it is a waw, check for letter on the right
							{
								BELONGS_TO = 0;
								for (int around_yt = yt - 6; around_yt <= yt + 6; around_yt++)
									if ((source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt) != WHITE ) && (connCmpPtr[source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt)].is_secondery == false))
										BELONGS_TO = source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 +2, around_yt);
							}//else
						}//if
					}//if
				}//if

				NT = 0;
				xtraverse = 0;

				//Check if main body below midpoint
				//there is no need for comments in this loop, to understand it refer to "check if main body above midpoint" 
				for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )
					if( (source->pixel(xmid, yt) != WHITE) && (connCmpPtr[source->pixel(xmid, yt)].is_secondery == false) )
					{
						BELONGS_TO = source->pixel(xmid,yt);
						if (connCmpPtr[source->pixel(xmid,yt)].height < y_size/1.6 && connCmpPtr[source->pixel(xmid,yt)].height >= y_size/3.6 )
						{
							if (maxX2 > (connCmpPtr[source->pixel(xmid,yt)].x2 - (connCmpPtr[source->pixel(xmid,yt)].x2 - connCmpPtr[source->pixel(xmid,yt)].x1)/ 8)) 
							{
								if (connCmpPtr[source->pixel(xmid,yt)].groupSecondery != -9)
								{
									int whiteSpaces;
									bool flag = false;
									for (whiteSpaces = connCmpPtr[source->pixel(xmid,yt)].x1 - 1; (whiteSpaces > (connCmpPtr[source->pixel(xmid,yt)].x1 - 10)) && flag == false; whiteSpaces--)
										for ( int ytt = 0; ytt < y_size; ytt++)
											if (source->pixel(whiteSpaces,ytt) != WHITE)
											{
												flag = true;
												break;
											}

											if (flag == false)			
											{
												for (int xtr = connCmpPtr[source->pixel(xmid, yt)].x1; xtr <=connCmpPtr[source->pixel(xmid,yt)].x2 - 5 ; xtr++)
													if (source->pixel(xtr,connCmpPtr[source->pixel(xmid,yt)].y2) != WHITE && connCmpPtr[source->pixel(xtr,connCmpPtr[source->pixel(xmid,yt)].y2)].is_secondery == false)
													{
														xtraverse = xtr;
													}
													for (int ii =0; ii<=3; ii++)
													{
														prev_y2 = connCmpPtr[source->pixel(xmid,yt)].y2;
														for(int ytraverse = connCmpPtr[source->pixel(xmid, yt)].y2; (ytraverse>=0) ; ytraverse--)
														{
															if (source->pixel(xtraverse,ytraverse) != WHITE && source->pixel(xtraverse,prev_y2) == WHITE && connCmpPtr[source->pixel(xtraverse, ytraverse)].is_secondery == false)
																NT++;
															prev_y2= ytraverse;
														}
														if (NT >= 2)
														{	
															BELONGS_TO = 0;
															for (int around_yt = yt - 6; around_yt <= yt + 6; around_yt++)
																if ((source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt) != WHITE ) && (connCmpPtr[source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt)].is_secondery == false))
																	BELONGS_TO = source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 +2, around_yt);

															connCmpPtr[source->pixel(xmid,yt)].groupSecondery = -9;
															break;
														}
														else 
														{
															xtraverse = xtraverse+1;
															NT = 0;
														}
													}//for
											}//if flag
								}//if -9
								else 
								{
									BELONGS_TO = 0;
									for (int around_yt = yt - 6; around_yt <= yt + 6; around_yt++)
										if ((source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt) != WHITE ) && (connCmpPtr[source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 + 2, around_yt)].is_secondery == false))
											BELONGS_TO = source->pixel(connCmpPtr[source->pixel(xmid,yt)].x2 +2, around_yt);
								}
							}//if
						}//if
					}//if

					//Check if secondery covers a main body and has a larger width than it
					for (int k = 1; (k <= no_of_cmps) && (BELONGS_TO == 0); k++) 
						if ((connCmpPtr[dec_x2_codes[k]].is_secondery == false) && (connCmpPtr[dec_x2_codes[k]].x1 > minX1) && (connCmpPtr[dec_x2_codes[k]].x2 < maxX2))
							BELONGS_TO = dec_x2_codes[k];


					//Check if main body above from the right endpoint
					for( int yt = minY1; (yt < y_size) && (BELONGS_TO == 0); yt++ )	
						if( (source->pixel(maxX2, yt) != WHITE) && (connCmpPtr[source->pixel(maxX2, yt)].is_secondery == false) )
							BELONGS_TO = source->pixel(maxX2,yt);


					//Check if main body above from the left endpoint
					for( int yt = minY1; (yt < y_size) && (BELONGS_TO == 0); yt++ )	
						if( (source->pixel(minX1, yt) != WHITE) && (connCmpPtr[source->pixel(minX1, yt)].is_secondery == false) )
							BELONGS_TO = source->pixel(minX1, yt);


					//Check if main body below from the right endpoint
					for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )		
						if( (source->pixel(maxX2, yt) != WHITE) && (connCmpPtr[source->pixel(maxX2, yt)].is_secondery == false) )
							BELONGS_TO = source->pixel(maxX2, yt);


					//Check if main body below from the left endpoint	
					for( int yt = minY1; (yt >= 0) && (BELONGS_TO == 0); yt-- )		
						if( (source->pixel(minX1, yt) != WHITE) && (connCmpPtr[source->pixel(minX1, yt)].is_secondery == false) )
							BELONGS_TO = source->pixel(minX1, yt);


					//Check if secondery comes before the first main body
					for (int j=1; (j <= no_of_cmps) && (BELONGS_TO == 0); j++)
						if (connCmpPtr[inc_x1_codes[j]].is_secondery == false)
				  {
					  if (minX1 < connCmpPtr[inc_x1_codes[j]].x1 )
						  BELONGS_TO = inc_x1_codes[j];
					  break; //must have break so that loop doesn't go beyond first component that isn't a secondery
				  }


						//Check if secondery comes after last main body
						for (int j=1; (j <= no_of_cmps) && (BELONGS_TO == 0); j++)
							if( connCmpPtr[dec_x2_codes[j]].is_secondery == false )
							{
								if (maxX2 > connCmpPtr[dec_x2_codes[j]].x2)	
									BELONGS_TO = dec_x2_codes[j];
								break;//must have break so that loop doesn't go beyond first component that isn't a secondery
							}


							//Check for nearest main body (body with nearest x value)
							if (BELONGS_TO == 0)
							{  
								for (r = i + 1 ; r <= no_of_cmps; r++)
									if (connCmpPtr[inc_x1_codes[r]].is_secondery == false)
									{
										distance1 = connCmpPtr[inc_x1_codes[r]].x1 - maxX2;//distance between secondery and component to the right
										break;
									}
									for (l = i - 1; l >= 1; l--)
										if (connCmpPtr[inc_x1_codes[l]].is_secondery == false)
										{
											distance2 = minX1 - connCmpPtr[inc_x1_codes[l]].x2;//distance between secondery and component to the left
											break;
										}
										if ((distance1 != 0) && (distance2 != 0))//to make sure there actually are components on the secondery's sides
										{
											if (distance2 < distance1)
												BELONGS_TO = inc_x1_codes[l];
											else 
												BELONGS_TO = inc_x1_codes[r];
										}
							}

							//Check for main body to the right
							for( int xt = maxX2 + 1; (xt < image_x2) && (BELONGS_TO == 0); xt++ ) 
								for( int yt = y_size - 1; (yt >= 0) && (BELONGS_TO == 0); yt-- ) 
									if( (source->pixel(xt, yt) != WHITE) && (connCmpPtr[source->pixel(xt, yt)].is_secondery == false) )
										BELONGS_TO = source->pixel(xt, yt); 

		}
	}


	delete []inc_x1_x1s;
	delete []dec_x2_x2s;

#undef X1				
#undef X2				
#undef Y1			
#undef Y2					
#undef IS_SEC			
#undef BELONGS_TO
#undef GROUP_SEC
}//End of Assign_Seconderies_Letters


DLLSYM subword* Extract_Letters(IMAGE* source,
								 int no_of_cmps,
								 connCmpInf* connCmpPtr,
								 BYTE* &inc_x1_codes,
								 BYTE* &dec_x2_codes,
								 int& no_of_subs)
{
#define SPACE 10		//Space between subwords //45 for some
#define MARGIN 5		//Space between rightmost subword and the right edge

	//Writing the IMAGE before extracting to a file for testing purposes
	//source->write("before_extracting.tif");

	//Variables:
	BYTE WHITE = source->get_white_level();
	int no_of_subwords = 0;
	int s = 1;					//To traverse the subwords							
	subword* subwordPtr;
	int x_size = source->get_xsize();
	int y_size = source->get_ysize();

	IMAGELINE line;
	line.init();
	
	//Finding the number of subwords = number of main bodies
	for( int i = 1; i <= no_of_cmps; i++ )
		if( connCmpPtr[i].is_secondery == false )
			no_of_subwords++;

	//Allocating a data structure to hold subwords info
	subwordPtr = new subword[no_of_subwords + 1];			//+1 to index subwords from 1

	//Filling the information of the subwords data structure
	for( int i = 1; i <= no_of_cmps; i++ ) {
		if( connCmpPtr[dec_x2_codes[i]].is_secondery == false ) {
			subwordPtr[s].color = dec_x2_codes[i];//25 Aug, 2009
			subwordPtr[s].x1 = connCmpPtr[dec_x2_codes[i]].x1;
			subwordPtr[s].x2 = connCmpPtr[dec_x2_codes[i]].x2;
			subwordPtr[s].y1 = connCmpPtr[dec_x2_codes[i]].y1;
			subwordPtr[s].y2 = connCmpPtr[dec_x2_codes[i]].y2;
			for( int j = 1; j <= no_of_cmps; j++ ) {
				if( connCmpPtr[j].belongs_to == dec_x2_codes[i] ) {
					if( connCmpPtr[j].x1 < subwordPtr[s].x1 ) subwordPtr[s].x1 = connCmpPtr[j].x1;
					if( connCmpPtr[j].x2 > subwordPtr[s].x2 ) subwordPtr[s].x2 = connCmpPtr[j].x2;
					if( connCmpPtr[j].y1 < subwordPtr[s].y1 ) subwordPtr[s].y1 = connCmpPtr[j].y1;
					if( connCmpPtr[j].y2 > subwordPtr[s].y2 ) subwordPtr[s].y2 = connCmpPtr[j].y2;
				}
			}
			s++;
		}
	}
		

	IMAGE tmpImg;
	tmpImg.create(x_size, y_size, 8);
	invert_image(&tmpImg);
	//copy_sub_image(source, 0, 0, x_size, y_size, &tmpImg, 0, 0, TRUE);
	//Extracting subwords
	int tmpDist;
	s = 1;

	if( !(subwordPtr[s].x2 > x_size/2 + 100) ) {			//This is to check if the image is accessible or not
		int dist = x_size - subwordPtr[s].x2 - MARGIN;			//Distance to shift the subword
		for( int i = 1; i <= no_of_cmps; i++ ) {
			if( connCmpPtr[dec_x2_codes[i]].belongs_to == 0 ) {
				for( int xt = connCmpPtr[dec_x2_codes[i]].x2; xt >= connCmpPtr[dec_x2_codes[i]].x1; xt-- ) {
					for( int yt = connCmpPtr[dec_x2_codes[i]].y1; yt <= connCmpPtr[dec_x2_codes[i]].y2; yt++ ) {
						if( source->pixel(xt, yt) == dec_x2_codes[i] ) {
							source->get_line(xt, yt, 1, &line, 0);
							line.pixels[0] = dec_x2_codes[i];
							tmpImg.put_line(xt + dist, yt, 1, &line, 0);
							line.pixels[0] = WHITE;
							tmpImg.put_line(xt, yt, 1, &line, 0);
						}
					}
				}

				connCmpPtr[dec_x2_codes[i]].x1 += dist;			//Updating the values stored in the connCmpPtr 
				connCmpPtr[dec_x2_codes[i]].x2 += dist;			//data structure for main components
				for( int j = 1; j <= no_of_cmps; j++) {
					if( connCmpPtr[j].belongs_to == dec_x2_codes[i]) {
						for( int xt = connCmpPtr[j].x1; xt <= connCmpPtr[j].x2; xt++ ) {
							for( int yt = connCmpPtr[j].y1; yt <= connCmpPtr[j].y2; yt++ ) {
								if( source->pixel(xt, yt) == j ) {
									source->get_line(xt, yt, 1, &line, 0);
									line.pixels[0] = j;
									tmpImg.put_line(xt + dist, yt, 1, &line, 0);
									line.pixels[0] = WHITE;
									tmpImg.put_line(xt, yt, 1, &line, 0);
								}
							}
						}
						connCmpPtr[j].x1 += dist;				//Updating the values stored in the connCmpPtr
						connCmpPtr[j].x2 += dist;				//data structure for seconderies
					}
				}
				tmpDist = dist;
				if( s <= no_of_subwords-1 ) {
					dist = dist - (subwordPtr[s].x2 - subwordPtr[s].x1 + 1) + (subwordPtr[s].x2 - subwordPtr[s+1].x2);
					if( (subwordPtr[s].x1 - subwordPtr[s+1].x2) >= SPACE )	//If the original distance between the 
						dist -= (subwordPtr[s].x1 - subwordPtr[s+1].x2);	//subwords is larger than space then
					else													//make it the new distance else make
						dist -= SPACE;										//it SPACE
				}
				subwordPtr[s].x1 += tmpDist;					//Updating the values stored in the subwordPtr
				subwordPtr[s].x2 += tmpDist;					//data structure
				s++;
			}
		}
	}
	else
	{
		s = no_of_subwords;
		int dist = subwordPtr[s].x1 - MARGIN;			//Distance to shift the subword
		for( int i = 1; i <= no_of_cmps; i++ ) {
			if( connCmpPtr[inc_x1_codes[i]].belongs_to == 0 ) {
				for( int xt = connCmpPtr[inc_x1_codes[i]].x1; xt <= connCmpPtr[inc_x1_codes[i]].x2; xt++ ) {
					for( int yt = connCmpPtr[inc_x1_codes[i]].y1; yt <= connCmpPtr[inc_x1_codes[i]].y2; yt++ ) {
						if( source->pixel(xt, yt) == inc_x1_codes[i] ) {
							source->get_line(xt, yt, 1, &line, 0);
							line.pixels[0] = inc_x1_codes[i];
							tmpImg.put_line(abs(xt - dist), yt, 1, &line, 0);
							line.pixels[0] = WHITE;
							tmpImg.put_line(xt, yt, 1, &line, 0);
						}
					}
				}
				connCmpPtr[inc_x1_codes[i]].x1 -= dist;			//Updating the values stored in the connCmpPtr 
				connCmpPtr[inc_x1_codes[i]].x2 -= dist;			//data structure for main components
				for( int j = 1; j <= no_of_cmps; j++) {
					if( connCmpPtr[j].belongs_to == inc_x1_codes[i]) {
						for( int xt = connCmpPtr[j].x1; xt <= connCmpPtr[j].x2; xt++ ) {
							for( int yt = connCmpPtr[j].y1; yt <= connCmpPtr[j].y2; yt++ ) {
								if( source->pixel(xt, yt) == j ) {
									source->get_line(xt, yt, 1, &line, 0);
									line.pixels[0] = j;
									tmpImg.put_line(abs(xt - dist), yt, 1, &line, 0);
									line.pixels[0] = WHITE;
									tmpImg.put_line(xt, yt, 1, &line, 0);
								}
							}
						}
						connCmpPtr[j].x1 -= dist;				//Updating the values stored in the connCmpPtr
						connCmpPtr[j].x2 -= dist;				//data structure for seconderies
					}
				}
				tmpDist = dist;
				if( s > 1 ) {
					dist = dist - (subwordPtr[s].x2 - subwordPtr[s].x1 + 1) + (subwordPtr[s-1].x1 - subwordPtr[s].x1);
					if( (subwordPtr[s-1].x1 - subwordPtr[s].x2) >= SPACE )	//If the original distance between the 
						dist -= (subwordPtr[s-1].x1 - subwordPtr[s].x2);	//subwords is larger than space then
					else													//make it the new distance else make
						dist -= SPACE;										//it SPACE
				}
				subwordPtr[s].x1 -= tmpDist;					//Updating the values stored in the subwordPtr
				subwordPtr[s].x2 -= tmpDist;					//data structure
				s--;
			}

		}
	}

	no_of_subs = no_of_subwords;			//Returning the number of subwords in the IMAGE to be used by
											//Simple_Segmentation


	for(int xt = 0; xt < x_size; xt++) {
		line.pixels[xt] = WHITE;
	}
	for(int yt = 0; yt < y_size; yt++) {					//clear source image
		source->put_line(0, yt, x_size, &line, 0);
	}

	int		distToShift = 0;
	for(int i = 1; i <= no_of_cmps; i++) {//making overlap between seconderies and their main bodies
		distToShift = 0;
		line.pixels[0] = i;
		if(connCmpPtr[i].is_secondery == true) {
			if(connCmpPtr[i].x2 < connCmpPtr[connCmpPtr[i].belongs_to].x1)
				distToShift = connCmpPtr[connCmpPtr[i].belongs_to].x1 - (connCmpPtr[i].x1 + connCmpPtr[i].x2)/2;
			if(connCmpPtr[i].x1 > connCmpPtr[connCmpPtr[i].belongs_to].x2)
				distToShift = -((connCmpPtr[i].x1 + connCmpPtr[i].x2)/2 - connCmpPtr[connCmpPtr[i].belongs_to].x2);
		}
		for(int xt = connCmpPtr[i].x1; xt <= connCmpPtr[i].x2; xt++) {
			for(int yt = connCmpPtr[i].y1; yt <= connCmpPtr[i].y2; yt++) {
				if(tmpImg.pixel(xt, yt) != WHITE) {
					source->put_column(xt + distToShift, yt, 1, &line, 0);
				}
			}
		}
		connCmpPtr[i].x1 += distToShift;
		connCmpPtr[i].x2 += distToShift;
	}

	return subwordPtr;
	
#undef SPACE 
#undef MARGIN
}//End of Extract_Letters




DLLSYM connCmpInf* Connected_Components_OLD(IMAGE* source, int& no_of_cmps, const char* fileName)
{
//Variables definations and their initializations:
const int size_of_byte = 256;
int frequency[size_of_byte] = {0};


int x_size = source->get_xsize();
int y_size = source->get_ysize();

IMAGELINE line;
line.init();

IMAGE temp_img;

BYTE WHITE = source->get_white_level(); //WHITE and BLACK data type should match the bpp of the IMAGE
BYTE BLACK = !WHITE;

if (x_size > y_size) // Gheith Jul 12, 2008
for(int s = 0; s < x_size; s++)
line.pixels[s] = WHITE;
else
for(int s = 0; s < y_size; s++)
line.pixels[s] = WHITE;

int xt, yt, xt2, yt2; //Pixels Traversers

BYTE counter = 1; //Counter is the code to be given to each connected component

BYTE P1; //P stand for pixel in the Previous row.
BYTE P2; // P1 P2 P3
BYTE P3; // L * where * denotes the pixel being analyzed
BYTE L ; //L stand for pixel to the left.


//Initial Labeling:
for(yt = y_size - 2; yt >= 0; yt--) {
for(xt = 1; xt < x_size; xt++) {
if(source->pixel(xt,yt) == BLACK) {
P1 = source->pixel(xt-1,yt+1);
P2 = source->pixel( xt ,yt+1);
P3 = source->pixel(xt+1,yt+1);
L = source->pixel(xt-1,yt);

if( L != WHITE) {
source->get_line(xt, yt, 1, &line, 0);
line.pixels[0] = L;
source->put_line(xt, yt, 1, &line, 0);
frequency[L]++;
}
else {
if( P1 == WHITE && P2 == WHITE && P3 == WHITE ) {
source->get_line(xt, yt, 1, &line, 0);
line.pixels[0] = counter;
source->put_line(xt, yt, 1, &line, 0);
frequency[counter]++;
counter++;
}
else {
if( P1 != WHITE) {
source->get_line(xt, yt, 1, &line, 0);
line.pixels[0] = P1;
source->put_line(xt, yt, 1, &line, 0);;
frequency[P1]++;
}
else {
if( P2 != WHITE) {
source->get_line(xt, yt, 1, &line, 0);
line.pixels[0] = P2;
source->put_line(xt, yt, 1, &line, 0);;
frequency[P2]++;
}
else {
source->get_line(xt, yt, 1, &line, 0);
line.pixels[0] = P3;
source->put_line(xt, yt, 1, &line, 0);;
frequency[P3]++;
}
}
}
}
}
}
}

//Label_Unification
for(yt = y_size-2; yt >= 0; yt--) {
for(xt = 1; xt < x_size; xt++) {
if(source->pixel(xt,yt) != WHITE) {
P1 = source->pixel(xt-1,yt+1);
P2 = source->pixel( xt ,yt+1);
P3 = source->pixel(xt+1,yt+1);
L = source->pixel(xt-1,yt);

if( L != WHITE ) {
if(L != P1 && P1 != WHITE)
for(xt2 = 0; xt2< x_size; xt2++) //Replace P1 with L
for(yt2 = 0; yt2 < y_size; yt2++) {
BYTE T = source->pixel(xt2,yt2);
if(T == P1) {
source->get_line(xt2, yt2, 1, &line, 0);
line.pixels[0] = L;
source->put_line(xt2, yt2, 1, &line, 0);
frequency[L]++;
frequency[P1] = 0;
}
}

if(L != P2 && P2 != WHITE)
for(xt2 = 0; xt2< x_size; xt2++) //Replace P2 with L
for(yt2 = 0; yt2 < y_size; yt2++)
{
BYTE T = source->pixel(xt2,yt2);
if(T == P2)
{
source->get_line(xt2, yt2, 1, &line, 0);
line.pixels[0] = L;
source->put_line(xt2, yt2, 1, &line, 0);
frequency[L]++;
frequency[P2] = 0;
}
}

if(L != P3 && P3 != WHITE)
for(xt2 = 0; xt2< x_size; xt2++) //Replace P3 with L
for(yt2 = 0; yt2 < y_size; yt2++) {
BYTE T = source->pixel(xt2,yt2);
if(T == P3) {
source->get_line(xt2, yt2, 1, &line, 0);
line.pixels[0] = L;
source->put_line(xt2, yt2, 1, &line, 0);
frequency[L]++;
frequency[P3] = 0;
}
}
}
else {
if(P1 != WHITE && P3 != WHITE && P1 != P3)
for(xt2 = 0; xt2< x_size; xt2++) //Replace P3 with P1
for(yt2 = 0; yt2 < y_size; yt2++) {
BYTE T = source->pixel(xt2,yt2);
if(T == P3) {
source->get_line(xt2, yt2, 1, &line, 0);
line.pixels[0] = P1;
source->put_line(xt2, yt2, 1, &line, 0);
frequency[P1]++;
frequency[P3] = 0;
}
}
}
}
}
}


//Label_Reassignment
CByteArray Index;
Index.RemoveAll();

for(int n=0; n < size_of_byte; n++)
if(frequency[n] != 0)
Index.Add(n);

counter = 1;
BYTE element;

for(int index = 0; index < Index.GetSize(); index++) {
element = Index.GetAt(index);
if(counter != element)
for(xt2 = 0; xt2 < x_size; xt2++)
for(yt2 = 0; yt2 < y_size; yt2++) {
BYTE T = source->pixel(xt2,yt2);
if(T == element) {
source->get_line(xt2, yt2, 1, &line, 0);
line.pixels[0] = counter;
source->put_line(xt2, yt2, 1, &line, 0);
frequency[counter]++;
frequency[element] = 0;
}
}
counter++;
}

//Creating the data structure to store the connected components information:
connCmpInf* connCmpPtr;
//connCmpPtr = (connCmpInf*)malloc(sizeof(connCmpInf) * counter);
connCmpPtr = new connCmpInf[counter];



//The outer-most loop is to traverse each connected component, components codes
//start from 1 and ends at counter-1 :
int i , j;
for(int cnt = 1; cnt < counter; cnt++) {
//Storing the component information
connCmpPtr[cnt].pixelsCnt = frequency[cnt];

//Loops to find the corners of the box containing the connected components:
//Loop to find x1:
i=0; j=0;
while( (i < x_size) && (source->pixel(i++,j-1) != cnt) ) {
j=0;
while( (j < y_size) && (source->pixel(i,j++) != cnt) );
}
connCmpPtr[cnt].x1 = i-1;

//Loop to find y1:
i = connCmpPtr[cnt].x1 ; j=0;
while( (j < y_size) && (source->pixel(i-1,j++) != cnt) ) {
i = connCmpPtr[cnt].x1;
while( (i < x_size) && (source->pixel(i++,j) != cnt) );
}
connCmpPtr[cnt].y1 = j-1;


//Loop to find x2:
i = x_size-1; j = y_size-1;
while( (i >= connCmpPtr[cnt].x1) && (source->pixel(i--,j+1) != cnt) ) {
j = y_size-1;
while( (j >= connCmpPtr[cnt].y1) && (source->pixel(i,j--) != cnt) );
}
connCmpPtr[cnt].x2 = i+1;

//Loop to find y2:
i = connCmpPtr[cnt].x2; j = y_size-1;
while( (j >= connCmpPtr[cnt].y1) && (source->pixel(i+1,j--) != cnt) ) {
i = connCmpPtr[cnt].x2;
while( (i >= connCmpPtr[cnt].x1) && (source->pixel(i--,j) != cnt) );
}
connCmpPtr[cnt].y2 = j+1;

//Width and height calculations:
connCmpPtr[cnt].width = connCmpPtr[cnt].x2 - connCmpPtr[cnt].x1 + 1;
connCmpPtr[cnt].height = connCmpPtr[cnt].y2 - connCmpPtr[cnt].y1 + 1;

//Area calculation:
connCmpPtr[cnt].area = connCmpPtr[cnt].width * connCmpPtr[cnt].height;

//Density calculation:
connCmpPtr[cnt].density = (double)connCmpPtr[cnt].pixelsCnt / connCmpPtr[cnt].area;
}


//Test: remove component with code 1
/*for(int i = 0; i < y_size; i++)
for(int j = 0; j < x_size; j++)
if( source->pixel(j,i) == 1) {
source->get_line(j, i, 1, &line, 0);
line.pixels[0] = 150;
source->put_line(j, i, 1, &line, 0);
}
source->write("fuad_afterRemovingCode1.tif");
*/


//Retururning the outputs to the caller function:
no_of_cmps = counter - 1; //Storing the number of components


return connCmpPtr; //Returning a pointer to the data
//structure containing the
//connected components information
}//End of Connected_Components